Usage
Get number of trials by method, day, and user:
db <- db_connect()
counts <- dbGetQuery(db,"SELECT responses.method AS 'method',
DATE(responses.date + 3600, 'unixepoch') AS 'doy',
responses.user_id AS 'user',
COUNT(*) AS 'trials'
FROM 'responses'
GROUP BY responses.method,
DATE(responses.date + 3600, 'unixepoch'),
responses.user_id
")
db_disconnect(db)
setDT(counts)
Add a school year column (cutoff date: 1 August):
counts[, doy_posix := as.POSIXct(doy)]
counts[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
Add more sensible course names:
counts[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]
Total number of unique users by course and school year
counts[, .(unique_users = length(unique(user))), by = .(course, school_year)]
There was some overlap between courses/school years, so we can’t simply add these numbers.
By course:
counts[, .(unique_users = length(unique(user))), by = .(course)]
By school year in the French and English courses:
counts[course %in% c("French", "English"), .(unique_users = length(unique(user))), by = .(school_year)]
And total number of unique users in the French and English sample across both years:
counts[course %in% c("French", "English"), .(unique_users = length(unique(user)))]
Total number of trials by course
counts[, .(total_trials = sum(trials)), by = .(course, school_year)]
Number of trials by user
counts[, trials_user := sum(trials), by = .(course, user)]
ggplot(counts, aes(x = trials_user)) +
facet_wrap(~course, ncol = 1, scales = "free_y") +
geom_histogram(binwidth = 100) +
labs(x = "Number of trials by user",
y = NULL) +
theme_paper

Number of unique days by user
ggplot(counts[, .(N = length(unique(doy_posix))), by = c("user", "course")], aes(x = N)) +
facet_grid(course ~ ., scales = "free_y") +
geom_histogram(binwidth = 1) +
labs(x = "Aantal dagen met leeractiviteit",
y = "Aantal leerlingen") +
theme_paper

Total number of trials by day
Interpolate missing days:
doy_posix <- seq.POSIXt(from = counts[,min(doy_posix)], to = counts[,max(doy_posix)], by = "DSTday")
course <- counts[,unique(course)]
dates <- CJ(doy_posix, course)
counts <- merge(counts, dates, by = c("doy_posix", "course"), all = TRUE)
Count trials by day:
counts[, trials_total := sum(trials, na.rm = TRUE), by = .(course, doy_posix)]
counts_by_day <- counts[, .(trials_total = sum(trials, na.rm = TRUE)), by = .(course, doy_posix)]
ggplot(counts_by_day[course %in% c("English", "French"),],
aes(x = doy_posix, y = trials_total, colour = course)) +
geom_line() +
scale_x_datetime(date_breaks = "3 months", date_labels = "%e %b %Y") +
scale_y_continuous(labels = number_format) +
labs(x = NULL,
y = "Number of trials per day",
colour = "Course") +
theme_paper

Total number of trials by week
Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.
counts_by_day[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
counts_by_day[, trials_total_week := sum(trials_total, na.rm = TRUE), by = .(course, doy_posix_week)]
ggplot(counts_by_day[course %in% c("English", "French"),],
aes(x = doy_posix, y = trials_total_week, colour = course)) +
geom_line() +
scale_x_datetime(date_breaks = "3 months", date_labels = "%e %b %Y") +
scale_y_continuous(labels = number_format) +
labs(x = NULL,
y = "Number of trials per week",
colour = "Course") +
theme_paper

Overlap the two school years:
counts_by_day[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
counts_by_day[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
counts_by_day[school_year == "19/20", doy_posix_aligned := doy_posix]
p_trial_hist <- ggplot(counts_by_day[course %in% c("English", "French"),],
aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week, group = school_year, colour = school_year, fill = school_year)) +
facet_wrap(~ course, ncol = 1) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(alpha = .2) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 2e6), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper
p_trial_hist

ggsave("../output/trial_hist.pdf", width = 5, height = 3)
ggsave("../output/trial_hist.png", width = 5, height = 3)
Make a line-plot version of the histogram.
p_trial_hist_line <- ggplot(counts_by_day[course %in% c("English", "French")],
aes(x = doy_posix_aligned, y = trials_total_week, group = school_year, colour = school_year, fill = school_year)) +
facet_wrap(~ course, ncol = 1) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_line() +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 2e6), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper
p_trial_hist_line
Warning: Removed 101 row(s) containing missing values (geom_path).

ggsave("../output/trial_hist_line.pdf", width = 5, height = 3)
Warning: Removed 101 row(s) containing missing values (geom_path).
ggsave("../output/trial_hist_line.png", width = 5, height = 3)
Warning: Removed 101 row(s) containing missing values (geom_path).
Also make a difference plot.
# In order for the Mondays to align, move the 18/19 data forward by 1 year - 1 day.
counts_by_day[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 364*24*60*60, origin = "1970-01-01")]
counts_by_day[school_year == "19/20", doy_posix_aligned := doy_posix]
counts_by_day[, year_diff := trials_total_week[2] - trials_total_week[1], by = .(course, doy_posix_aligned)]
ggplot(counts_by_day[course %in% c("English", "French")],
aes(x = doy_posix_aligned, y = year_diff)) +
facet_wrap(~ course, ncol = 1) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -1e6, ymax = 1.1e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_hline(yintercept = 0, lty = 3) +
geom_line() +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(-3e5, 1e6), labels = number_format) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper
Warning: Removed 101 row(s) containing missing values (geom_path).

Number of trials by user and week
counts[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
counts_by_user_and_week <- counts[, .(trials_user = sum(trials, na.rm = TRUE)), by = .(course, school_year, user, doy_posix_week)]
Save for clustering analysis
saveRDS(na.omit(counts_by_user_and_week[course %in% c("English", "French")]), "../data/trials_by_user_and_week.rds")
Unique users by day
users_by_day <- counts[, .(unique_users = length(unique(user))), by = .(course, doy_posix)]
p <- ggplot(users_by_day, aes(x = doy_posix, y = unique_users, colour = course)) +
geom_line() +
scale_x_datetime(date_breaks = "3 months", date_labels = "%e %b %Y") +
scale_y_continuous(labels = number_format) +
labs(x = NULL,
y = "Number of users per day",
colour = "Course") +
theme_paper
p

Unique users by week
Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.
users_by_day[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
users_by_week <- counts[, .(unique_users_week = length(unique(user))), by = .(course, doy_posix_week)]
users_by_week <- users_by_day[users_by_week, on = .(course, doy_posix_week)]
p <- ggplot(users_by_week, aes(x = doy_posix, ymin = 0, ymax = unique_users_week, group = course, colour = course, fill = course)) +
facet_wrap(~ course, ncol = 1, scales = "free_y") +
geom_ribbon(alpha = .2) +
scale_x_datetime(date_breaks = "3 months", date_labels = "%e %b %Y") +
scale_y_continuous(labels = scales::number_format(big.mark = ".", decimal.mark = ",")) +
labs(x = NULL,
y = "Aantal gebruikers",
title = "Aantal verschillende gebruikers per week",
caption = "Let op: schaal verschilt tussen de grafieken",
colour = "Lesmethode",
fill = "Lesmethode") +
guides(colour = FALSE, fill = FALSE) +
theme_paper
p

Overlap the two school years:
users_by_week[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
users_by_week[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
users_by_week[school_year == "19/20", doy_posix_aligned := doy_posix]
p_user_hist <- ggplot(users_by_week[course %in% c("English", "French"),],
aes(x = doy_posix_aligned, ymin = 0, ymax = unique_users_week, group = school_year, colour = school_year, fill = school_year)) +
facet_wrap(~ course, ncol = 1) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -800, ymax = 8800, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(alpha = .2) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 8000), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Unique users per week",
colour = "School year",
fill = "School year") +
theme_paper
p_user_hist

ggsave("../output/user_hist.pdf", width = 5, height = 3)
ggsave("../output/user_hist.png", width = 5, height = 3)
Make a combined plot of trial and user counts for in the paper:
p_legend <- get_legend(p_trial_hist)
p_trial_hist <- p_trial_hist +
guides(colour = FALSE, fill = FALSE)
p_user_hist <- p_user_hist +
guides(colour = FALSE, fill = FALSE)
plot_grid(plot_grid(p_trial_hist, p_user_hist,
labels = c("A", "B"),
align = "v", axis = "tblr"),
p_legend,
rel_widths = c(1, .2))

ggsave("../output/combi_hist.pdf", width = 9, height = 3)
ggsave("../output/combi_hist.png", width = 9, height = 3)
Activity during the week
Get number of trials by method, day, hour, and user:
db <- db_connect()
counts_by_hour <- dbGetQuery(db,"SELECT responses.method AS 'method',
DATE(responses.date + 3600, 'unixepoch') AS 'doy',
STRFTIME('%H', responses.date + 3600, 'unixepoch') AS 'hour',
responses.user_id AS 'user',
COUNT(*) AS 'trials'
FROM 'responses'
GROUP BY responses.method,
DATE(responses.date + 3600, 'unixepoch'),
STRFTIME('%H', responses.date + 3600, 'unixepoch'),
responses.user_id
")
db_disconnect(db)
setDT(counts_by_hour)
Interpolate missing days and hours:
counts_by_hour[, doy_posix := as.POSIXct(doy)]
counts_by_hour[, hour := as.numeric(hour)]
doy_posix <- seq.POSIXt(from = counts_by_hour[,min(doy_posix)], to = counts_by_hour[,max(doy_posix)], by = "DSTday")
method <- counts_by_hour[,unique(method)]
hour <- 0:23
dates_and_hours <- CJ(doy_posix, hour, method)
counts_by_hour <- merge(counts_by_hour, dates_and_hours, by = c("doy_posix", "hour", "method"), all = TRUE)
Add day of the week:
counts_by_hour[, weekday := weekdays(doy_posix)]
Distinguish between school years:
counts_by_hour[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
Add quarter:
counts_by_hour[, quarter := paste0(year(doy_posix), "Q", quarter(doy_posix))]
Add exact school closure period in both school years:
counts_by_hour[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
counts_by_hour[school_year == "19/20", doy_posix_aligned := doy_posix]
counts_by_hour[, schools_closed := doy_posix_aligned >= date_schools_closed & doy_posix_aligned < date_schools_opened]
Add more sensible course names:
counts_by_hour[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]
Sum trials by school year, weekday and hour:
counts_by_hour[, trials_schoolyear := sum(trials, na.rm = TRUE), by = .(course, school_year, weekday, hour)]
Also sum trials by quarter, weekday and hour:
counts_by_hour[, trials_quarter := sum(trials, na.rm = TRUE), by = .(course, quarter, weekday, hour)]
And sum trials within the closure period by weekday and hour:
counts_by_hour[schools_closed == TRUE, trials_closed := sum(trials, na.rm = TRUE), by = .(course, school_year, weekday, hour)]
trials_by_wday_hour <- unique(counts_by_hour, by = c("course", "school_year", "quarter", "schools_closed", "weekday", "hour"))
trials_by_wday_hour[, trials_normalised_schoolyear := trials_schoolyear / sum(trials_schoolyear), by = .(course)]
trials_by_wday_hour[, trials_normalised_quarter := trials_quarter / sum(trials_quarter), by = .(course)]
trials_by_wday_hour[, weekday := ordered(weekday, levels = c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"))]
# trials_by_wday_hour[, weekday := ordered(weekday, levels = c("maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag", "zondag"))]
Plot heatmap for the whole school year:
ggplot(trials_by_wday_hour[course %in% c("English", "French")],
aes(x = hour, y = reorder(weekday, dplyr::desc(weekday)), fill = trials_normalised_schoolyear)) +
facet_grid(school_year ~ course) +
geom_tile(colour = "white", size = 0.25) +
labs(x = "Time of day (hour)",
y = NULL) +
scale_x_continuous(expand = c(0,0), breaks = seq(0, 24, 3)) +
scale_y_discrete(expand = c(0,0)) +
scale_fill_viridis_c(option = "A", direction = -1) +
coord_fixed() +
guides(fill = FALSE) +
theme_paper

Plot heatmap per quarter:
ggplot(trials_by_wday_hour, aes(x = hour, y = reorder(weekday, dplyr::desc(weekday)), fill = trials_normalised_quarter)) +
facet_grid(quarter ~ method) +
geom_tile(colour = "white", size = 0.25) +
labs(x = NULL,
y = NULL,
title = "Activiteit per uur gedurende de week",
caption = "Aantal trials per weekdag en uur in elk kwartaal, genormaliseerd per methode.") +
scale_x_continuous(expand = c(0,0), breaks = seq(0, 24, 3)) +
scale_y_discrete(expand = c(0,0)) +
scale_fill_viridis_c(option = "A", direction = -1) +
coord_fixed() +
guides(fill = FALSE) +
theme_bw(base_size = 16)

Plot heatmap for the period in which schools were closed:
trials_closed <- unique(trials_by_wday_hour[schools_closed == TRUE, .(course, school_year, weekday, hour, trials_closed)])
trials_closed[, trials_normalised_closed := trials_closed / sum(trials_closed), by = .(course, school_year)]
trials_closed_diff <- trials_closed[, .(school_year = "Change",
trials_closed = trials_closed[school_year == "19/20"] - trials_closed[school_year == "18/19"],
trials_normalised_closed = trials_normalised_closed[school_year == "19/20"] - trials_normalised_closed[school_year == "18/19"]), by = .(course, weekday, hour)]
p_heatmap <- ggplot(trials_closed[course %in% c("English", "French"),],
aes(x = hour, y = reorder(weekday, dplyr::desc(weekday)), fill = trials_normalised_closed)) +
facet_grid(school_year ~ course) +
geom_tile(colour = "white", size = 0.25) +
labs(x = "Time of day (hour)",
y = NULL,
fill = NULL) +
scale_x_continuous(expand = c(0,0), breaks = seq(0, 24, 3)) +
scale_y_discrete(expand = c(0,0)) +
scale_fill_viridis_c(option = "A", direction = -1) +
coord_fixed() +
theme_paper
p_heatmap

Make a plot of the difference between the two school years during the school closure period:
p_heatmap_diff <- ggplot(trials_closed_diff[course %in% c("English", "French"),],
aes(x = hour, y = reorder(weekday, dplyr::desc(weekday)), fill = trials_normalised_closed)) +
facet_grid(school_year ~ course) +
geom_tile(colour = "white", size = 0.25) +
labs(x = "Time of day (hour)",
y = NULL,
fill = NULL) +
scale_x_continuous(expand = c(0,0), breaks = seq(0, 24, 3)) +
scale_y_discrete(expand = c(0,0)) +
scale_fill_distiller(type = "div", palette = "RdBu", direction = -1, limits = c(-1, 1) * max(abs(trials_closed_diff[course %in% c("English", "French"),]$trials_normalised_closed))) +
coord_fixed() +
theme_paper
p_heatmap_diff

Make a combined plot for in the paper:
p_heatmap_legend <- get_legend(p_heatmap)
p_heatmap_diff_legend <- get_legend(p_heatmap_diff)
p_heatmap <- p_heatmap + guides(fill = FALSE)
p_heatmap_diff <- p_heatmap_diff + guides(fill = FALSE)
plot_grid(
plot_grid(p_heatmap, p_heatmap_diff,
ncol = 1,
labels = c("A", "B"),
rel_heights = c(1, .655)
),
plot_grid(p_heatmap_legend, p_heatmap_diff_legend,
ncol = 1,
align = "vh", axis = "lrtb"),
ncol = 2,
rel_widths = c(1, .15))

ggsave("../output/combi_heatmap.pdf", width = 9, height = 4.5)
ggsave("../output/combi_heatmap.png", width = 9, height = 4.5)
Activity stratified by year and level
db <- db_connect()
counts_strat <- dbGetQuery(db,"SELECT responses.method AS 'method',
responses.book_info_id as 'book_info_id',
DATE(responses.date + 3600, 'unixepoch') AS 'doy',
responses.user_id AS 'user',
COUNT(*) AS 'trials'
FROM 'responses'
GROUP BY responses.method, responses.book_info_id,
DATE(responses.date + 3600, 'unixepoch'),
responses.user_id
")
db_disconnect(db)
setDT(counts_strat)
db <- db_connect()
book_info <- dbGetQuery(db, "SELECT * FROM 'book_info'")
db_disconnect(db)
setDT(book_info)
Add book information:
counts_strat[book_info, on = "book_info_id", c("book_title", "method_group") := .(i.book_title, i.method_group)]
Add a school year column (cutoff date: 1 August):
counts_strat[, doy_posix := as.POSIXct(doy)]
counts_strat[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
Add sensible course names:
counts_strat[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]
Count trials by day:
counts_strat_by_day <- counts_strat[, .(trials_total = sum(trials, na.rm = TRUE)), by = .(school_year, course, method_group, book_title, doy_posix)]
setorder(counts_strat_by_day, school_year, course, method_group, book_title, doy_posix)
Simplify level names:
# Keep all distinctions
counts_strat_by_day[, book_title_simple := stringr::str_sub(book_title, 3, -10)]
counts_strat_by_day[, book_title_simple := factor(book_title_simple, levels = c("vmbo b/lwoo", "vmbo b", "vmbo bk", "vmbo k", "vmbo kgt", "vmbo-gt", "vmbo gt", "vmbo-gt/havo", "vmbo (t)hv", "havo", "havo vwo", "vwo"))]
# Simplify to three levels
counts_strat_by_day[, level := dplyr::case_when(
grepl( "hv", book_title) ~ "General secondary\n(havo)",
grepl("vmbo", book_title) ~ "Pre-vocational\n(vmbo)",
grepl("havo", book_title) ~ "General secondary\n(havo)",
grepl("vwo", book_title) ~ "Pre-university\n(vwo)",
TRUE ~ "Other")]
counts_strat_by_day[, level := factor(level, levels = c("Other", "Pre-vocational\n(vmbo)", "General secondary\n(havo)", "Pre-university\n(vwo)"))]
Simplify year names:
counts_strat_by_day[, year := dplyr::case_when(
method_group == "Leerjaar 1 (5e Ed.)" ~ "Year 1",
method_group == "Leerjaar 2 (5e Ed.)" ~ "Year 2",
method_group == "Leerjaar 3 (5e Ed.)" ~ "Year 3",
method_group == "Leerjaar 3/4 (5e Ed.)" ~ "Year 3/4",
method_group == "Leerjaar 4 (5e Ed.)" ~ "Year 4",
method_group == "Tweede Fase (6e Ed.)" ~ "Tweede Fase",
TRUE ~ "Other")]
Align school years:
counts_strat_by_day[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
counts_strat_by_day[school_year == "19/20", doy_posix_aligned := doy_posix]
Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.
counts_strat_by_day[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
counts_strat_by_day[, trials_total_week := sum(trials_total, na.rm = TRUE), by = .(school_year, course, method_group, book_title_simple, doy_posix_aligned_week)]
counts_strat_by_day[, trials_total_week_level := sum(trials_total), by = .(school_year, course, method_group, level, doy_posix_aligned_week)]
Summarise increase during lockdown:
counts_strat_increase <- counts_strat_by_day[between(doy_posix_aligned, date_schools_closed, date_schools_opened), .(trials_lockdown = sum(trials_total)), by = .(course, book_title_simple, method_group, year, school_year)]
counts_strat_increase[, increase := trials_lockdown[2]/trials_lockdown[1], by = .(course, book_title_simple, method_group, year)]
counts_strat_increase[, increase_pct := paste0("Change:\n", scales::percent(increase, accuracy = 2))]
counts_strat_increase_level <- counts_strat_by_day[between(doy_posix_aligned, date_schools_closed, date_schools_opened), .(trials_lockdown = sum(trials_total)), by = .(course, level, method_group, year, school_year)]
counts_strat_increase_level[, increase := trials_lockdown[2]/trials_lockdown[1], by = .(course, level, method_group, year)]
counts_strat_increase_level[, increase_pct := paste0("Change:\n", scales::percent(increase, accuracy = 2))]
French
ggplot(counts_strat_by_day[course == "French"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(book_title_simple ~ method_group) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week, ), alpha = .2) +
geom_text(data = counts_strat_increase[course == "French" & school_year == "19/20"],
aes(label = increase_pct),
x = as.POSIXct((as.numeric(date_schools_closed) + as.numeric(date_schools_opened))/2, origin = "1970-01-01"),
y = 3.6e5,
colour = "black",
vjust = 1,
show.legend = FALSE) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 3.75e5), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year",
title = "French") +
theme_paper

ggsave("../output/trial_hist_french.pdf", width = 14, height = 10)
ggsave("../output/trial_hist_french.png", width = 14, height = 10)
Streamlined version for in the paper:
ggplot(counts_strat_by_day[course == "French"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(level ~ year) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week_level, ), alpha = .2) +
geom_text(data = counts_strat_increase_level[course == "French" & school_year == "19/20"],
aes(label = increase_pct),
x = as.POSIXct((as.numeric(date_schools_closed) + as.numeric(date_schools_opened))/2, origin = "1970-01-01"),
y = 3.6e5,
colour = "black",
vjust = 1,
size = rel(2.75),
show.legend = FALSE) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-10-01 02:00:00 CET",
"2019-12-01 02:00:00 CET",
"2020-02-01 02:00:00 CET",
"2020-04-01 02:00:00 CET",
"2020-06-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 3.75e5), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper

ggsave("../output/trial_hist_french_level.pdf", width = 9, height = 5)
ggsave("../output/trial_hist_french_level.png", width = 9, height = 5)
English
ggplot(counts_strat_by_day[course == "English"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(book_title_simple ~ method_group) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week, ), alpha = .2) +
geom_text(data = counts_strat_increase[course == "English" & school_year == "19/20"],
aes(label = increase_pct),
x = as.POSIXct((as.numeric(date_schools_closed) + as.numeric(date_schools_opened))/2, origin = "1970-01-01"),
y = 3.6e5,
colour = "black",
vjust = 1,
show.legend = FALSE) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-09-01 02:00:00 CET",
"2019-11-01 02:00:00 CET",
"2020-01-01 02:00:00 CET",
"2020-03-01 02:00:00 CET",
"2020-05-01 02:00:00 CET",
"2020-07-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 3.75e5), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year",
title = "English") +
theme_paper

ggsave("../output/trial_hist_english.pdf", width = 14, height = 10)
ggsave("../output/trial_hist_english.png", width = 14, height = 10)
Streamlined version for in the paper:
ggplot(counts_strat_by_day[course == "English" & level != "Other"],
aes(group = school_year, colour = school_year, fill = school_year)) +
facet_grid(level ~ year) +
geom_rect(xmin = date_schools_closed, xmax = date_schools_opened, ymin = -2e5, ymax = 2.2e6, fill = "grey92", colour = "grey50", lty = 2) +
geom_ribbon(aes(x = doy_posix_aligned, ymin = 0, ymax = trials_total_week_level, ), alpha = .2) +
geom_text(data = counts_strat_increase_level[course == "English" & level != "Other" & school_year == "19/20"],
aes(label = increase_pct),
x = as.POSIXct((as.numeric(date_schools_closed) + as.numeric(date_schools_opened))/2, origin = "1970-01-01"),
y = 9.6e5,
colour = "black",
vjust = 1,
size = rel(2.75),
show.legend = FALSE) +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-10-01 02:00:00 CET",
"2019-12-01 02:00:00 CET",
"2020-02-01 02:00:00 CET",
"2020-04-01 02:00:00 CET",
"2020-06-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
scale_y_continuous(expand = c(0, 0), limits = c(0, 1e6), labels = number_format) +
scale_colour_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
scale_fill_viridis_d(end = .5, direction = -1, na.translate = FALSE) +
labs(x = NULL,
y = "Trials per week",
colour = "School year",
fill = "School year") +
theme_paper

ggsave("../output/trial_hist_english_level.pdf", width = 9, height = 5)
ggsave("../output/trial_hist_english_level.png", width = 9, height = 5)
Question type
There are different question formats: open-answer, in which the student types the answer, and multiple-choice, in which the student selects the answer from a set of 3 or 4 options.
db <- db_connect()
question_type <- dbGetQuery(db,
"SELECT r.method AS 'method',
DATE(r.date + 3600, 'unixepoch') AS 'doy',
r.choices AS 'choices',
COUNT(*) AS 'n'
FROM 'responses' r
WHERE r.study == 0
GROUP BY r.method,
DATE(r.date + 3600, 'unixepoch'),
r.choices"
)
setDT(question_type)
db_disconnect(db)
Add a school year column (cutoff date: 1 August):
question_type[, doy_posix := as.POSIXct(doy)]
question_type[, school_year := ifelse(doy_posix < "2019-08-01", "18/19", "19/20")]
Add sensible course names:
question_type[, course := ifelse(method == "Grandes Lignes", "French", ifelse(method == "Stepping Stones", "English", "German"))]
Align school years:
question_type[school_year == "18/19", doy_posix_aligned := as.POSIXct(doy_posix + 365*24*60*60, origin = "1970-01-01")]
question_type[school_year == "19/20", doy_posix_aligned := doy_posix]
Use cut.Date() to bin dates by week. Each day is assigned the date of the most recent Monday.
question_type[, doy_posix_week := cut.POSIXt(doy_posix, "week")]
question_type[, doy_posix_aligned_week := cut.POSIXt(doy_posix_aligned, "week")]
question_type_by_week <- question_type[, .(n = sum(n)), by = .(course, school_year, doy_posix_aligned_week, choices)]
ggplot(question_type_by_week[course %in% c("English", "French")], aes(x = as.POSIXct(doy_posix_aligned_week), y = n, group = interaction(school_year,as.factor(choices)), colour = school_year)) +
facet_grid(course ~ choices) +
geom_line() +
scale_x_datetime(expand = c(0, 0),
breaks = as.POSIXct(c(
"2019-10-01 02:00:00 CET",
"2019-12-01 02:00:00 CET",
"2020-02-01 02:00:00 CET",
"2020-04-01 02:00:00 CET",
"2020-06-01 02:00:00 CET")),
limits = as.POSIXct(c("2019-09-01 02:00:00 CET", "2020-07-01 02:00:00 CET")),
date_labels = "%b") +
labs(x = NULL,
y = "Trials",
colour = "School year") +
theme_paper
Warning: Removed 24 row(s) containing missing values (geom_path).

question_type[, .(n = sum(n)), by = .(course, mcq = choices>1, school_year)][, .(perc_mcq = n[mcq == TRUE]/sum(n)), by = .(course, school_year)]
There is a clear difference between the languages in the question format used: English uses almost exclusively 4-alternative MCQs, while French uses a mix of MCQs (including a small number of 3-alternative questions) and open-answer questions.
LS0tCnRpdGxlOiAnU2xpbVN0YW1wZW4gVXNhZ2UgRHVyaW5nIExvY2tkb3duJwphdXRob3I6ICJNYWFydGVuIHZhbiBkZXIgVmVsZGUiCmRhdGU6ICJMYXN0IHVwZGF0ZWQ6IGByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgZ2l0aHViX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBodG1sX25vdGVib29rOgogICAgc21hcnQ6IG5vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIFNldHVwCgpgYGB7cn0KbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KERCSSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ3JpZCkKClN5cy5zZXRsb2NhbGUoIkxDX1RJTUUiLCAiZW5fVVMuVVRGLTgiKSAjIFByaW50IEVuZ2xpc2ggZGF0ZSBmb3JtYXQKIyBTeXMuc2V0bG9jYWxlKCJMQ19USU1FIiwgIm5sX05MLlVURi04IikgIyBQcmludCBEdXRjaCBkYXRlIGZvcm1hdAoKbnVtYmVyX2Zvcm1hdCA8LSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYmlnLm1hcmsgPSAiLCIsIGRlY2ltYWwubWFyayA9ICIuIikgIyBQcmludCBFbmdsaXNoIG51bWJlciBmb3JtYXQKIyBudW1iZXJfZm9ybWF0IDwtIHNjYWxlczo6bnVtYmVyX2Zvcm1hdChiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiKSAjIFByaW50IER1dGNoIG51bWJlciBmb3JtYXQKCnRoZW1lX3BhcGVyIDwtIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTIpICsgCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfbGluZShjb2xvdXIgPSAiZ3JleTkyIikpCmBgYAoKU2Nob29sIGNsb3N1cmUgYW5kIG9wZW5pbmcgZGF0ZXMKClNvdXJjZXM6Ci0gaHR0cHM6Ly93d3cucmlqa3NvdmVyaGVpZC5ubC9hY3R1ZWVsL25pZXV3cy8yMDIwLzAzLzE1L2FhbnZ1bGxlbmRlLW1hYXRyZWdlbGVuLW9uZGVyd2lqcy1ob3JlY2Etc3BvcnQKLSBodHRwczovL3d3dy5yaWprc292ZXJoZWlkLm5sL2FjdHVlZWwvbmlldXdzLzIwMjAvMDUvMTkvb25kZXJ3aWpzLWdhYXQtc3RhcC12b29yLXN0YXAtb3BlbgpgYGB7cn0KZGF0ZV9zY2hvb2xzX2Nsb3NlZCA8LSBhcy5QT1NJWGN0KCIyMDIwLTAzLTE2IikKZGF0ZV9zY2hvb2xzX29wZW5lZCA8LSBhcy5QT1NJWGN0KCIyMDIwLTA2LTAyIikKYGBgCgoKSGFuZGxlIGRhdGFiYXNlIGNvbm5lY3Rpb25zCmBgYHtyfQpkYl9jb25uZWN0IDwtIGZ1bmN0aW9uKCkgewogIGRiIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgZmlsZS5wYXRoKCIuLiIsICJkYXRhIiwgIm5vb3JkaG9mZi5zcWxpdGUiKSkKICByZXR1cm4oZGIpCn0KCmRiX2Rpc2Nvbm5lY3QgPC0gZnVuY3Rpb24oZGIpIHsKICBkYkRpc2Nvbm5lY3QoZGIpCn0KYGBgCgoKIyBEYXRhCgpUaGUgZGF0YWJhc2UgY29udGFpbnMgYWxsIFNsaW1TdGFtcGVuIGRhdGEgY29sbGVjdGVkIHZpYSBOb29yZGhvZmYncyBwbGF0Zm9ybSBpbiB0aHJlZSBjb3Vyc2VzOiAqU3RlcHBpbmcgU3RvbmVzKiAoRW5nbGlzaCksICpHcmFuZGVzIExpZ25lcyogKEZyZW5jaCksIGFuZCAqTmV1ZSBLb250YWt0ZSogKEdlcm1hbikuCgpUcmlhbC1sZXZlbCByZXNwb25zZSBkYXRhIGFyZSBzdG9yZWQgaW4gdGhlIGByZXNwb25zZXNgIHRhYmxlLgpCb29rIGluZm9ybWF0aW9uLCBzdWNoIGFzIHRoZSBjb3Vyc2UgeWVhciwgYm9vayB0aXRsZSwgYW5kIGNoYXB0ZXIsIGFyZSBzdG9yZWQgaW4gdGhlIGBib29rX2luZm9gIHRhYmxlLgoKIyMgYHJlc3BvbnNlc2AKCnwgQ29sdW1uICAgICAgICAgICAgICAgfCBUeXBlICAgICAgfCBFeHBsYW5hdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBkYXRlICAgICAgICAgICAgICAgICB8IGludCAgICAgICB8IFVOSVggdGltZSBzdGFtcCBbc10gICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgdXNlcl9pZCAgICAgICAgICAgICAgfCBjaHIgICAgICAgfCB1bmlxdWUgdXNlciBpZGVudGlmaWVyICAgICAgICAgICAgICAgICAgICAgICAgfAp8IG1ldGhvZCAgICAgICAgICAgICAgIHwgY2hyICAgICAgIHwgY291cnNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBzdGFydF90aW1lICAgICAgICAgICB8IGludCAgICAgICB8IGVsYXBzZWQgdGltZSBzaW5jZSBzZXNzaW9uIHN0YXJ0IFttc10gICAgICAgICB8CnwgcnQgICAgICAgICAgICAgICAgICAgfCBpbnQgICAgICAgfCByZXNwb25zZSB0aW1lIFttc10gICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGR1cmF0aW9uICAgICAgICAgICAgIHwgaW50ICAgICAgIHwgdHJpYWwgZHVyYXRpb24gW21zXSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBmYWN0X2lkICAgICAgICAgICAgICB8IGludCAgICAgICB8IHVuaXF1ZSBmYWN0IGlkZW50aWZpZXIgKHdpdGhpbiBjaGFwdGVyKSAgICAgICB8CnwgY29ycmVjdCAgICAgICAgICAgICAgfCBpbnQgICAgICAgfCByZXNwb25zZSBhY2N1cmFjeSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGFuc3dlciAgICAgICAgICAgICAgIHwgY2hyICAgICAgIHwgdXNlcidzIHJlc3BvbnNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBjaG9pY2VzICAgICAgICAgICAgICB8IGludCAgICAgICB8IG51bWJlciBvZiBhbnN3ZXIgY2hvaWNlcyAoMSA9PSBvcGVuIHJlc3BvbnNlKSB8CnwgYmFja3NwYWNlX3VzZWQgICAgICAgfCBkYmwgICAgICAgfCB1c2VyIHByZXNzZWQgYmFja3NwYWNlIGR1cmluZyB0cmlhbCAgICAgICAgICAgfAp8IGJhY2tzcGFjZV91c2VkX2ZpcnN0IHwgZGJsICAgICAgIHwgdXNlciBlcmFzZWQgZmlyc3QgY2hhcmFjdGVyIG9mIHJlc3BvbnNlICAgICAgIHwKfCBzdHVkeSAgICAgICAgICAgICAgICB8IGludCAgICAgICB8IHRyaWFsIHdhcyBhIHN0dWR5IHRyaWFsICAgICAgICAgICAgICAgICAgICAgICB8CnwgYW5zd2VyX2xhbmd1YWdlICAgICAgfCBjaHIgICAgICAgfCBsYW5ndWFnZSBvZiB0aGUgYW5zd2VyICAgICAgICAgICAgICAgICAgICAgICAgfAp8IHN1YnNlc3Npb24gICAgICAgICAgIHwgaW50ICAgICAgIHwgaWRlbnRpZmllcyBwYXJ0IHdpdGhpbiBsZWFybmluZyBzZXNzaW9uICAgICAgIHwKfCBib29rX2luZm9faWQgICAgICAgICB8IGNociAgICAgICB8IHVuaXF1ZSBpZGVudGlmaWVyIG9mIGJvb2sgaW5mb3JtYXRpb24gICAgICAgICB8CgoKIyMgYGJvb2tfaW5mb2AKCnwgQ29sdW1uICAgICAgICAgICAgICAgfCBUeXBlICAgICAgfCBFeHBsYW5hdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBib29rX2luZm9faWQgICAgICAgICB8IGNociAgICAgICB8IHVuaXF1ZSBpZGVudGlmaWVyIG9mIGJvb2sgaW5mb3JtYXRpb24gICAgICAgICB8CnwgbWV0aG9kX2dyb3VwICAgICAgICAgfCBjaHIgICAgICAgfCB5ZWFyIGFuZCBlZGl0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGJvb2tfdGl0bGUgICAgICAgICAgIHwgY2hyICAgICAgIHwgYm9vayB0aXRsZSAoaW5jbC4geWVhciwgbGV2ZWwsIGVkaXRpb24pICAgICAgIHwKfCBib29rX3R5cGUgICAgICAgICAgICB8IGNociAgICAgICB8IHR5cGUgb2YgYm9vayAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgY2hhcHRlciAgICAgICAgICAgICAgfCBjaHIgICAgICAgfCBjaGFwdGVyIG51bWJlciBhbmQgdGl0bGUgICAgICAgICAgICAgICAgICAgICAgfAoKClByZXZpZXcgZmlyc3QgMTAgcm93cwpgYGB7cn0KZGIgPC0gZGJfY29ubmVjdCgpCnJlc3BvbnNlc190b3AgPC0gZGJHZXRRdWVyeShkYiwgIlNFTEVDVCAqIEZST00gcmVzcG9uc2VzIExJTUlUIDEwIikKcmVzcG9uc2VzX3RvcAoKYm9va19pbmZvX3RvcCA8LSBkYkdldFF1ZXJ5KGRiLCAiU0VMRUNUICogRlJPTSBib29rX2luZm8gTElNSVQgMTAiKQpib29rX2luZm9fdG9wCmRiX2Rpc2Nvbm5lY3QoZGIpCmBgYAoKCgojIFVzYWdlCgpHZXQgbnVtYmVyIG9mIHRyaWFscyBieSBtZXRob2QsIGRheSwgYW5kIHVzZXI6CmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKY291bnRzIDwtIGRiR2V0UXVlcnkoZGIsIlNFTEVDVCByZXNwb25zZXMubWV0aG9kIEFTICdtZXRob2QnLAogICAgICAgICAgICAgICAgICAgICAgICAgIERBVEUocmVzcG9uc2VzLmRhdGUgKyAzNjAwLCAndW5peGVwb2NoJykgQVMgJ2RveScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzcG9uc2VzLnVzZXJfaWQgQVMgJ3VzZXInLAogICAgICAgICAgICAgICAgICAgICAgICAgIENPVU5UKCopIEFTICd0cmlhbHMnCiAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSAncmVzcG9uc2VzJwogICAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIHJlc3BvbnNlcy5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgREFURShyZXNwb25zZXMuZGF0ZSAgKyAzNjAwLCAndW5peGVwb2NoJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzcG9uc2VzLnVzZXJfaWQKICAgICAgICAgICAgICAgICAgICAgICAgIikKZGJfZGlzY29ubmVjdChkYikKCnNldERUKGNvdW50cykKYGBgCgpBZGQgYSBzY2hvb2wgeWVhciBjb2x1bW4gKGN1dG9mZiBkYXRlOiAxIEF1Z3VzdCk6CmBgYHtyfQpjb3VudHNbLCBkb3lfcG9zaXggOj0gYXMuUE9TSVhjdChkb3kpXQpjb3VudHNbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KYGBgCgpBZGQgbW9yZSBzZW5zaWJsZSBjb3Vyc2UgbmFtZXM6CmBgYHtyfQpjb3VudHNbLCBjb3Vyc2UgOj0gaWZlbHNlKG1ldGhvZCA9PSAiR3JhbmRlcyBMaWduZXMiLCAiRnJlbmNoIiwgaWZlbHNlKG1ldGhvZCA9PSAiU3RlcHBpbmcgU3RvbmVzIiwgIkVuZ2xpc2giLCAiR2VybWFuIikpXQpgYGAKCgojIyBUb3RhbCBudW1iZXIgb2YgdW5pcXVlIHVzZXJzIGJ5IGNvdXJzZSBhbmQgc2Nob29sIHllYXIKYGBge3J9CmNvdW50c1ssIC4odW5pcXVlX3VzZXJzID0gbGVuZ3RoKHVuaXF1ZSh1c2VyKSkpLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhcildCmBgYAoKVGhlcmUgd2FzIHNvbWUgb3ZlcmxhcCBiZXR3ZWVuIGNvdXJzZXMvc2Nob29sIHllYXJzLCBzbyB3ZSBjYW4ndCBzaW1wbHkgYWRkIHRoZXNlIG51bWJlcnMuCgpCeSBjb3Vyc2U6CmBgYHtyfQpjb3VudHNbLCAuKHVuaXF1ZV91c2VycyA9IGxlbmd0aCh1bmlxdWUodXNlcikpKSwgYnkgPSAuKGNvdXJzZSldCmBgYAoKCkJ5IHNjaG9vbCB5ZWFyIGluIHRoZSBGcmVuY2ggYW5kIEVuZ2xpc2ggY291cnNlczoKYGBge3J9CmNvdW50c1tjb3Vyc2UgJWluJSBjKCJGcmVuY2giLCAiRW5nbGlzaCIpLCAuKHVuaXF1ZV91c2VycyA9IGxlbmd0aCh1bmlxdWUodXNlcikpKSwgYnkgPSAuKHNjaG9vbF95ZWFyKV0KYGBgCgpBbmQgdG90YWwgbnVtYmVyIG9mIHVuaXF1ZSB1c2VycyBpbiB0aGUgRnJlbmNoIGFuZCBFbmdsaXNoIHNhbXBsZSBhY3Jvc3MgYm90aCB5ZWFyczoKYGBge3J9CmNvdW50c1tjb3Vyc2UgJWluJSBjKCJGcmVuY2giLCAiRW5nbGlzaCIpLCAuKHVuaXF1ZV91c2VycyA9IGxlbmd0aCh1bmlxdWUodXNlcikpKV0KYGBgCgoKIyMgVG90YWwgbnVtYmVyIG9mIHRyaWFscyBieSBjb3Vyc2UKYGBge3J9CmNvdW50c1ssIC4odG90YWxfdHJpYWxzID0gc3VtKHRyaWFscykpLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhcildCmBgYAoKIyMgTnVtYmVyIG9mIHRyaWFscyBieSB1c2VyCgpgYGB7cn0KY291bnRzWywgdHJpYWxzX3VzZXIgOj0gc3VtKHRyaWFscyksIGJ5ID0gLihjb3Vyc2UsIHVzZXIpXQpnZ3Bsb3QoY291bnRzLCBhZXMoeCA9IHRyaWFsc191c2VyKSkgKwogIGZhY2V0X3dyYXAofmNvdXJzZSwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDApICsKICBsYWJzKHggPSAiTnVtYmVyIG9mIHRyaWFscyBieSB1c2VyIiwKICAgICAgIHkgPSBOVUxMKSArCiAgdGhlbWVfcGFwZXIKCmBgYAoKIyMgTnVtYmVyIG9mIHVuaXF1ZSBkYXlzIGJ5IHVzZXIKYGBge3J9CmdncGxvdChjb3VudHNbLCAuKE4gPSBsZW5ndGgodW5pcXVlKGRveV9wb3NpeCkpKSwgYnkgPSBjKCJ1c2VyIiwgImNvdXJzZSIpXSwgYWVzKHggPSBOKSkgKwogIGZhY2V0X2dyaWQoY291cnNlIH4gLiwgc2NhbGVzID0gImZyZWVfeSIpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpICsKICBsYWJzKHggPSAiQWFudGFsIGRhZ2VuIG1ldCBsZWVyYWN0aXZpdGVpdCIsCiAgICAgICB5ID0gIkFhbnRhbCBsZWVybGluZ2VuIikgKwogIHRoZW1lX3BhcGVyCmBgYAoKCiMjIFRvdGFsIG51bWJlciBvZiB0cmlhbHMgYnkgZGF5CgpJbnRlcnBvbGF0ZSBtaXNzaW5nIGRheXM6CmBgYHtyfQpkb3lfcG9zaXggPC0gc2VxLlBPU0lYdChmcm9tID0gY291bnRzWyxtaW4oZG95X3Bvc2l4KV0sIHRvID0gY291bnRzWyxtYXgoZG95X3Bvc2l4KV0sIGJ5ID0gIkRTVGRheSIpCmNvdXJzZSA8LSBjb3VudHNbLHVuaXF1ZShjb3Vyc2UpXQpkYXRlcyA8LSBDSihkb3lfcG9zaXgsIGNvdXJzZSkKY291bnRzIDwtIG1lcmdlKGNvdW50cywgZGF0ZXMsIGJ5ID0gYygiZG95X3Bvc2l4IiwgImNvdXJzZSIpLCBhbGwgPSBUUlVFKQpgYGAKCkNvdW50IHRyaWFscyBieSBkYXk6CmBgYHtyfQpjb3VudHNbLCB0cmlhbHNfdG90YWwgOj0gc3VtKHRyaWFscywgbmEucm0gPSBUUlVFKSwgYnkgPSAuKGNvdXJzZSwgZG95X3Bvc2l4KV0KYGBgCgpgYGB7cn0KY291bnRzX2J5X2RheSA8LSBjb3VudHNbLCAuKHRyaWFsc190b3RhbCA9IHN1bSh0cmlhbHMsIG5hLnJtID0gVFJVRSkpLCBieSA9IC4oY291cnNlLCBkb3lfcG9zaXgpXQpgYGAKCgpgYGB7cn0KZ2dwbG90KGNvdW50c19ieV9kYXlbY291cnNlICVpbiUgYygiRW5nbGlzaCIsICJGcmVuY2giKSxdLAogICAgICAgYWVzKHggPSBkb3lfcG9zaXgsIHkgPSB0cmlhbHNfdG90YWwsIGNvbG91ciA9IGNvdXJzZSkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeF9kYXRldGltZShkYXRlX2JyZWFrcyA9ICIzIG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiVlICViICVZIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiTnVtYmVyIG9mIHRyaWFscyBwZXIgZGF5IiwKICAgICAgIGNvbG91ciA9ICJDb3Vyc2UiKSArCiAgdGhlbWVfcGFwZXIKYGBgCgoKIyMgVG90YWwgbnVtYmVyIG9mIHRyaWFscyBieSB3ZWVrCgpVc2UgY3V0LkRhdGUoKSB0byBiaW4gZGF0ZXMgYnkgd2Vlay4gRWFjaCBkYXkgaXMgYXNzaWduZWQgdGhlIGRhdGUgb2YgdGhlIG1vc3QgcmVjZW50IE1vbmRheS4KYGBge3J9CmNvdW50c19ieV9kYXlbLCBkb3lfcG9zaXhfd2VlayA6PSBjdXQuUE9TSVh0KGRveV9wb3NpeCwgIndlZWsiKV0KY291bnRzX2J5X2RheVssIHRyaWFsc190b3RhbF93ZWVrIDo9IHN1bSh0cmlhbHNfdG90YWwsIG5hLnJtID0gVFJVRSksIGJ5ID0gLihjb3Vyc2UsIGRveV9wb3NpeF93ZWVrKV0KYGBgCgoKYGBge3J9CmdncGxvdChjb3VudHNfYnlfZGF5W2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIiksXSwKICAgICAgICAgICAgYWVzKHggPSBkb3lfcG9zaXgsIHkgPSB0cmlhbHNfdG90YWxfd2VlaywgY29sb3VyID0gY291cnNlKSkgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV94X2RhdGV0aW1lKGRhdGVfYnJlYWtzID0gIjMgbW9udGhzIiwgZGF0ZV9sYWJlbHMgPSAiJWUgJWIgJVkiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IG51bWJlcl9mb3JtYXQpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJOdW1iZXIgb2YgdHJpYWxzIHBlciB3ZWVrIiwKICAgICAgIGNvbG91ciA9ICJDb3Vyc2UiKSArCiAgdGhlbWVfcGFwZXIKYGBgCgoKT3ZlcmxhcCB0aGUgdHdvIHNjaG9vbCB5ZWFyczoKYGBge3J9CmNvdW50c19ieV9kYXlbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KY291bnRzX2J5X2RheVtzY2hvb2xfeWVhciA9PSAiMTgvMTkiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGRveV9wb3NpeCArIDM2NSoyNCo2MCo2MCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKV0KY291bnRzX2J5X2RheVtzY2hvb2xfeWVhciA9PSAiMTkvMjAiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBkb3lfcG9zaXhdCmBgYAoKYGBge3J9CnBfdHJpYWxfaGlzdCA8LSBnZ3Bsb3QoY291bnRzX2J5X2RheVtjb3Vyc2UgJWluJSBjKCJFbmdsaXNoIiwgIkZyZW5jaCIpLF0sCiAgICAgICAgICAgIGFlcyh4ID0gZG95X3Bvc2l4X2FsaWduZWQsIHltaW4gPSAwLCB5bWF4ID0gdHJpYWxzX3RvdGFsX3dlZWssIGdyb3VwID0gc2Nob29sX3llYXIsIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfd3JhcCh+IGNvdXJzZSwgbmNvbCA9IDEpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTJlNSwgeW1heCA9IDIuMmU2LCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyKSArCiAgZ2VvbV9yaWJib24oYWxwaGEgPSAuMikgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTExLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAxLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAzLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA1LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCAyZTYpLCBsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlRyaWFscyBwZXIgd2VlayIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIpICsKICB0aGVtZV9wYXBlcgoKcF90cmlhbF9oaXN0CgpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0LnBkZiIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMykKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdC5wbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmBgYAoKTWFrZSBhIGxpbmUtcGxvdCB2ZXJzaW9uIG9mIHRoZSBoaXN0b2dyYW0uCmBgYHtyfQpwX3RyaWFsX2hpc3RfbGluZSA8LSBnZ3Bsb3QoY291bnRzX2J5X2RheVtjb3Vyc2UgJWluJSBjKCJFbmdsaXNoIiwgIkZyZW5jaCIpXSwKICAgICAgICAgICAgYWVzKHggPSBkb3lfcG9zaXhfYWxpZ25lZCwgeSA9IHRyaWFsc190b3RhbF93ZWVrLCBncm91cCA9IHNjaG9vbF95ZWFyLCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgZmlsbCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X3dyYXAofiBjb3Vyc2UsIG5jb2wgPSAxKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0yZTUsIHltYXggPSAyLjJlNiwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMikgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMy0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMmU2KSwgbGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJUcmlhbHMgcGVyIHdlZWsiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiKSArCiAgdGhlbWVfcGFwZXIKCnBfdHJpYWxfaGlzdF9saW5lCgpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0X2xpbmUucGRmIiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0X2xpbmUucG5nIiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQpgYGAKCkFsc28gbWFrZSBhIGRpZmZlcmVuY2UgcGxvdC4KYGBge3J9CiMgSW4gb3JkZXIgZm9yIHRoZSBNb25kYXlzIHRvIGFsaWduLCBtb3ZlIHRoZSAxOC8xOSBkYXRhIGZvcndhcmQgYnkgMSB5ZWFyIC0gMSBkYXkuCmNvdW50c19ieV9kYXlbc2Nob29sX3llYXIgPT0gIjE4LzE5IiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gYXMuUE9TSVhjdChkb3lfcG9zaXggKyAzNjQqMjQqNjAqNjAsIG9yaWdpbiA9ICIxOTcwLTAxLTAxIildCmNvdW50c19ieV9kYXlbc2Nob29sX3llYXIgPT0gIjE5LzIwIiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gZG95X3Bvc2l4XQpgYGAKCmBgYHtyfQpjb3VudHNfYnlfZGF5WywgeWVhcl9kaWZmIDo9IHRyaWFsc190b3RhbF93ZWVrWzJdIC0gdHJpYWxzX3RvdGFsX3dlZWtbMV0sIGJ5ID0gLihjb3Vyc2UsIGRveV9wb3NpeF9hbGlnbmVkKV0KCmdncGxvdChjb3VudHNfYnlfZGF5W2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIildLAogICAgICAgICAgICBhZXMoeCA9IGRveV9wb3NpeF9hbGlnbmVkLCB5ID0geWVhcl9kaWZmKSkgKwogIGZhY2V0X3dyYXAofiBjb3Vyc2UsIG5jb2wgPSAxKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0xZTYsIHltYXggPSAxLjFlNiwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9IDMpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeF9kYXRldGltZShleHBhbmQgPSBjKDAsIDApLCAKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTEtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDEtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDMtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDUtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKC0zZTUsIDFlNiksIGxhYmVscyA9IG51bWJlcl9mb3JtYXQpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJUcmlhbHMgcGVyIHdlZWsiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiKSArCiAgdGhlbWVfcGFwZXIKYGBgCgoKIyMgTnVtYmVyIG9mIHRyaWFscyBieSB1c2VyIGFuZCB3ZWVrCmBgYHtyfQpjb3VudHNbLCBkb3lfcG9zaXhfd2VlayA6PSBjdXQuUE9TSVh0KGRveV9wb3NpeCwgIndlZWsiKV0KY291bnRzX2J5X3VzZXJfYW5kX3dlZWsgPC0gY291bnRzWywgLih0cmlhbHNfdXNlciA9IHN1bSh0cmlhbHMsIG5hLnJtID0gVFJVRSkpLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhciwgdXNlciwgZG95X3Bvc2l4X3dlZWspXQpgYGAKClNhdmUgZm9yIGNsdXN0ZXJpbmcgYW5hbHlzaXMKYGBge3J9CnNhdmVSRFMobmEub21pdChjb3VudHNfYnlfdXNlcl9hbmRfd2Vla1tjb3Vyc2UgJWluJSBjKCJFbmdsaXNoIiwgIkZyZW5jaCIpXSksICIuLi9kYXRhL3RyaWFsc19ieV91c2VyX2FuZF93ZWVrLnJkcyIpCmBgYAoKCiMjIFVuaXF1ZSB1c2VycyBieSBkYXkKCmBgYHtyfQp1c2Vyc19ieV9kYXkgPC0gY291bnRzWywgLih1bmlxdWVfdXNlcnMgPSBsZW5ndGgodW5pcXVlKHVzZXIpKSksIGJ5ID0gLihjb3Vyc2UsIGRveV9wb3NpeCldCmBgYAoKYGBge3J9CnAgPC0gZ2dwbG90KHVzZXJzX2J5X2RheSwgYWVzKHggPSBkb3lfcG9zaXgsIHkgPSB1bmlxdWVfdXNlcnMsIGNvbG91ciA9IGNvdXJzZSkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeF9kYXRldGltZShkYXRlX2JyZWFrcyA9ICIzIG1vbnRocyIsIGRhdGVfbGFiZWxzID0gIiVlICViICVZIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiTnVtYmVyIG9mIHVzZXJzIHBlciBkYXkiLAogICAgICAgY29sb3VyID0gIkNvdXJzZSIpICsKICB0aGVtZV9wYXBlcgoKcApgYGAKCgojIyBVbmlxdWUgdXNlcnMgYnkgd2VlawoKVXNlIGN1dC5EYXRlKCkgdG8gYmluIGRhdGVzIGJ5IHdlZWsuIEVhY2ggZGF5IGlzIGFzc2lnbmVkIHRoZSBkYXRlIG9mIHRoZSBtb3N0IHJlY2VudCBNb25kYXkuCmBgYHtyfQp1c2Vyc19ieV9kYXlbLCBkb3lfcG9zaXhfd2VlayA6PSBjdXQuUE9TSVh0KGRveV9wb3NpeCwgIndlZWsiKV0KYGBgCgpgYGB7cn0KdXNlcnNfYnlfd2VlayA8LSBjb3VudHNbLCAuKHVuaXF1ZV91c2Vyc193ZWVrID0gbGVuZ3RoKHVuaXF1ZSh1c2VyKSkpLCBieSA9IC4oY291cnNlLCBkb3lfcG9zaXhfd2VlayldCnVzZXJzX2J5X3dlZWsgPC0gdXNlcnNfYnlfZGF5W3VzZXJzX2J5X3dlZWssIG9uID0gLihjb3Vyc2UsIGRveV9wb3NpeF93ZWVrKV0KYGBgCgpgYGB7cn0KcCA8LSBnZ3Bsb3QodXNlcnNfYnlfd2VlaywgYWVzKHggPSBkb3lfcG9zaXgsIHltaW4gPSAwLCB5bWF4ID0gdW5pcXVlX3VzZXJzX3dlZWssIGdyb3VwID0gY291cnNlLCBjb2xvdXIgPSBjb3Vyc2UsIGZpbGwgPSBjb3Vyc2UpKSArCiAgZmFjZXRfd3JhcCh+IGNvdXJzZSwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgZ2VvbV9yaWJib24oYWxwaGEgPSAuMikgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZGF0ZV9icmVha3MgPSAiMyBtb250aHMiLCBkYXRlX2xhYmVscyA9ICIlZSAlYiAlWSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KGJpZy5tYXJrID0gIi4iLCBkZWNpbWFsLm1hcmsgPSAiLCIpKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiQWFudGFsIGdlYnJ1aWtlcnMiLAogICAgICAgdGl0bGUgPSAiQWFudGFsIHZlcnNjaGlsbGVuZGUgZ2VicnVpa2VycyBwZXIgd2VlayIsCiAgICAgICBjYXB0aW9uID0gIkxldCBvcDogc2NoYWFsIHZlcnNjaGlsdCB0dXNzZW4gZGUgZ3JhZmlla2VuIiwKICAgICAgIGNvbG91ciA9ICJMZXNtZXRob2RlIiwKICAgICAgIGZpbGwgPSAiTGVzbWV0aG9kZSIpICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UsIGZpbGwgPSBGQUxTRSkgKwogIHRoZW1lX3BhcGVyCgpwCmBgYAoKT3ZlcmxhcCB0aGUgdHdvIHNjaG9vbCB5ZWFyczoKYGBge3J9CnVzZXJzX2J5X3dlZWtbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KdXNlcnNfYnlfd2Vla1tzY2hvb2xfeWVhciA9PSAiMTgvMTkiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGRveV9wb3NpeCArIDM2NSoyNCo2MCo2MCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKV0KdXNlcnNfYnlfd2Vla1tzY2hvb2xfeWVhciA9PSAiMTkvMjAiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBkb3lfcG9zaXhdCmBgYAoKCmBgYHtyfQpwX3VzZXJfaGlzdCA8LSBnZ3Bsb3QodXNlcnNfYnlfd2Vla1tjb3Vyc2UgJWluJSBjKCJFbmdsaXNoIiwgIkZyZW5jaCIpLF0sCiAgICAgICAgICAgIGFlcyh4ID0gZG95X3Bvc2l4X2FsaWduZWQsIHltaW4gPSAwLCB5bWF4ID0gdW5pcXVlX3VzZXJzX3dlZWssIGdyb3VwID0gc2Nob29sX3llYXIsIGNvbG91ciA9IHNjaG9vbF95ZWFyLCBmaWxsID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfd3JhcCh+IGNvdXJzZSwgbmNvbCA9IDEpICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTgwMCwgeW1heCA9IDg4MDAsIGZpbGwgPSAiZ3JleTkyIiwgY29sb3VyID0gImdyZXk1MCIsIGx0eSA9IDIpICsKICBnZW9tX3JpYmJvbihhbHBoYSA9IC4yKSArCiAgc2NhbGVfeF9kYXRldGltZShleHBhbmQgPSBjKDAsIDApLCAKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTEtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDEtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDMtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDUtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDgwMDApLCBsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlVuaXF1ZSB1c2VycyBwZXIgd2VlayIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIpICsKICB0aGVtZV9wYXBlcgoKcF91c2VyX2hpc3QKCmdnc2F2ZSgiLi4vb3V0cHV0L3VzZXJfaGlzdC5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmdnc2F2ZSgiLi4vb3V0cHV0L3VzZXJfaGlzdC5wbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmBgYAoKCk1ha2UgYSBjb21iaW5lZCBwbG90IG9mIHRyaWFsIGFuZCB1c2VyIGNvdW50cyBmb3IgaW4gdGhlIHBhcGVyOgpgYGB7cn0KcF9sZWdlbmQgPC0gZ2V0X2xlZ2VuZChwX3RyaWFsX2hpc3QpCgpwX3RyaWFsX2hpc3QgPC0gcF90cmlhbF9oaXN0ICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UsIGZpbGwgPSBGQUxTRSkKCnBfdXNlcl9oaXN0IDwtIHBfdXNlcl9oaXN0ICsKICBndWlkZXMoY29sb3VyID0gRkFMU0UsIGZpbGwgPSBGQUxTRSkKYGBgCgpgYGB7cn0KcGxvdF9ncmlkKHBsb3RfZ3JpZChwX3RyaWFsX2hpc3QsIHBfdXNlcl9oaXN0LAogICAgICAgICAgbGFiZWxzID0gYygiQSIsICJCIiksCiAgICAgICAgICBhbGlnbiA9ICJ2IiwgYXhpcyA9ICJ0YmxyIiksCiAgICAgICAgICBwX2xlZ2VuZCwKICAgICAgICAgIHJlbF93aWR0aHMgPSBjKDEsIC4yKSkKCmdnc2F2ZSgiLi4vb3V0cHV0L2NvbWJpX2hpc3QucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSAzKQpnZ3NhdmUoIi4uL291dHB1dC9jb21iaV9oaXN0LnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gMykKYGBgCgoKIyMgQWN0aXZpdHkgZHVyaW5nIHRoZSB3ZWVrCgpHZXQgbnVtYmVyIG9mIHRyaWFscyBieSBtZXRob2QsIGRheSwgaG91ciwgYW5kIHVzZXI6CmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKY291bnRzX2J5X2hvdXIgPC0gZGJHZXRRdWVyeShkYiwiU0VMRUNUIHJlc3BvbnNlcy5tZXRob2QgQVMgJ21ldGhvZCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgREFURShyZXNwb25zZXMuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSBBUyAnZG95JywKICAgICAgICAgICAgICAgICAgICAgICAgICBTVFJGVElNRSgnJUgnLCByZXNwb25zZXMuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSBBUyAnaG91cicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzcG9uc2VzLnVzZXJfaWQgQVMgJ3VzZXInLAogICAgICAgICAgICAgICAgICAgICAgICAgIENPVU5UKCopIEFTICd0cmlhbHMnCiAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSAncmVzcG9uc2VzJwogICAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIHJlc3BvbnNlcy5tZXRob2QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgREFURShyZXNwb25zZXMuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBTVFJGVElNRSgnJUgnLCByZXNwb25zZXMuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByZXNwb25zZXMudXNlcl9pZAogICAgICAgICAgICAgICAgICAgICAgICAiKQpkYl9kaXNjb25uZWN0KGRiKQoKc2V0RFQoY291bnRzX2J5X2hvdXIpCmBgYAoKSW50ZXJwb2xhdGUgbWlzc2luZyBkYXlzIGFuZCBob3VyczoKYGBge3J9CmNvdW50c19ieV9ob3VyWywgZG95X3Bvc2l4IDo9IGFzLlBPU0lYY3QoZG95KV0KY291bnRzX2J5X2hvdXJbLCBob3VyIDo9IGFzLm51bWVyaWMoaG91cildCmRveV9wb3NpeCA8LSBzZXEuUE9TSVh0KGZyb20gPSBjb3VudHNfYnlfaG91clssbWluKGRveV9wb3NpeCldLCB0byA9IGNvdW50c19ieV9ob3VyWyxtYXgoZG95X3Bvc2l4KV0sIGJ5ID0gIkRTVGRheSIpCm1ldGhvZCA8LSBjb3VudHNfYnlfaG91clssdW5pcXVlKG1ldGhvZCldCmhvdXIgPC0gMDoyMwpkYXRlc19hbmRfaG91cnMgPC0gQ0ooZG95X3Bvc2l4LCBob3VyLCBtZXRob2QpCmNvdW50c19ieV9ob3VyIDwtIG1lcmdlKGNvdW50c19ieV9ob3VyLCBkYXRlc19hbmRfaG91cnMsIGJ5ID0gYygiZG95X3Bvc2l4IiwgImhvdXIiLCAibWV0aG9kIiksIGFsbCA9IFRSVUUpCmBgYAoKQWRkIGRheSBvZiB0aGUgd2VlazoKYGBge3J9CmNvdW50c19ieV9ob3VyWywgd2Vla2RheSA6PSB3ZWVrZGF5cyhkb3lfcG9zaXgpXQpgYGAKCkRpc3Rpbmd1aXNoIGJldHdlZW4gc2Nob29sIHllYXJzOgpgYGB7cn0KY291bnRzX2J5X2hvdXJbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KYGBgCgpBZGQgcXVhcnRlcjoKYGBge3J9CmNvdW50c19ieV9ob3VyWywgcXVhcnRlciA6PSBwYXN0ZTAoeWVhcihkb3lfcG9zaXgpLCAiUSIsIHF1YXJ0ZXIoZG95X3Bvc2l4KSldCmBgYAoKCkFkZCBleGFjdCBzY2hvb2wgY2xvc3VyZSBwZXJpb2QgaW4gYm90aCBzY2hvb2wgeWVhcnM6CmBgYHtyfQpjb3VudHNfYnlfaG91cltzY2hvb2xfeWVhciA9PSAiMTgvMTkiLCBkb3lfcG9zaXhfYWxpZ25lZCA6PSBhcy5QT1NJWGN0KGRveV9wb3NpeCArIDM2NSoyNCo2MCo2MCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKV0KY291bnRzX2J5X2hvdXJbc2Nob29sX3llYXIgPT0gIjE5LzIwIiwgZG95X3Bvc2l4X2FsaWduZWQgOj0gZG95X3Bvc2l4XQoKY291bnRzX2J5X2hvdXJbLCBzY2hvb2xzX2Nsb3NlZCA6PSBkb3lfcG9zaXhfYWxpZ25lZCA+PSBkYXRlX3NjaG9vbHNfY2xvc2VkICYgZG95X3Bvc2l4X2FsaWduZWQgPCBkYXRlX3NjaG9vbHNfb3BlbmVkXQpgYGAKCgpBZGQgbW9yZSBzZW5zaWJsZSBjb3Vyc2UgbmFtZXM6CmBgYHtyfQpjb3VudHNfYnlfaG91clssIGNvdXJzZSA6PSBpZmVsc2UobWV0aG9kID09ICJHcmFuZGVzIExpZ25lcyIsICJGcmVuY2giLCBpZmVsc2UobWV0aG9kID09ICJTdGVwcGluZyBTdG9uZXMiLCAiRW5nbGlzaCIsICJHZXJtYW4iKSldCmBgYAoKClN1bSB0cmlhbHMgYnkgc2Nob29sIHllYXIsIHdlZWtkYXkgYW5kIGhvdXI6CmBgYHtyfQpjb3VudHNfYnlfaG91clssIHRyaWFsc19zY2hvb2x5ZWFyIDo9IHN1bSh0cmlhbHMsIG5hLnJtID0gVFJVRSksIGJ5ID0gLihjb3Vyc2UsIHNjaG9vbF95ZWFyLCB3ZWVrZGF5LCBob3VyKV0KYGBgCgpBbHNvIHN1bSB0cmlhbHMgYnkgcXVhcnRlciwgd2Vla2RheSBhbmQgaG91cjoKYGBge3J9CmNvdW50c19ieV9ob3VyWywgdHJpYWxzX3F1YXJ0ZXIgOj0gc3VtKHRyaWFscywgbmEucm0gPSBUUlVFKSwgYnkgPSAuKGNvdXJzZSwgcXVhcnRlciwgd2Vla2RheSwgaG91cildCmBgYAoKQW5kIHN1bSB0cmlhbHMgd2l0aGluIHRoZSBjbG9zdXJlIHBlcmlvZCBieSB3ZWVrZGF5IGFuZCBob3VyOgpgYGB7cn0KY291bnRzX2J5X2hvdXJbc2Nob29sc19jbG9zZWQgPT0gVFJVRSwgdHJpYWxzX2Nsb3NlZCA6PSBzdW0odHJpYWxzLCBuYS5ybSA9IFRSVUUpLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhciwgd2Vla2RheSwgaG91cildCmBgYAoKCmBgYHtyfQp0cmlhbHNfYnlfd2RheV9ob3VyIDwtIHVuaXF1ZShjb3VudHNfYnlfaG91ciwgYnkgPSBjKCJjb3Vyc2UiLCAic2Nob29sX3llYXIiLCAicXVhcnRlciIsICJzY2hvb2xzX2Nsb3NlZCIsICJ3ZWVrZGF5IiwgImhvdXIiKSkKCnRyaWFsc19ieV93ZGF5X2hvdXJbLCB0cmlhbHNfbm9ybWFsaXNlZF9zY2hvb2x5ZWFyIDo9IHRyaWFsc19zY2hvb2x5ZWFyIC8gc3VtKHRyaWFsc19zY2hvb2x5ZWFyKSwgYnkgPSAuKGNvdXJzZSldCnRyaWFsc19ieV93ZGF5X2hvdXJbLCB0cmlhbHNfbm9ybWFsaXNlZF9xdWFydGVyIDo9IHRyaWFsc19xdWFydGVyIC8gc3VtKHRyaWFsc19xdWFydGVyKSwgYnkgPSAuKGNvdXJzZSldCgp0cmlhbHNfYnlfd2RheV9ob3VyWywgd2Vla2RheSA6PSBvcmRlcmVkKHdlZWtkYXksIGxldmVscyA9IGMoIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsICJUaHVyc2RheSIsICJGcmlkYXkiLCAiU2F0dXJkYXkiLCAiU3VuZGF5IikpXQojIHRyaWFsc19ieV93ZGF5X2hvdXJbLCB3ZWVrZGF5IDo9IG9yZGVyZWQod2Vla2RheSwgbGV2ZWxzID0gYygibWFhbmRhZyIsICJkaW5zZGFnIiwgIndvZW5zZGFnIiwgImRvbmRlcmRhZyIsICJ2cmlqZGFnIiwgInphdGVyZGFnIiwgInpvbmRhZyIpKV0KYGBgCgoKUGxvdCBoZWF0bWFwIGZvciB0aGUgd2hvbGUgc2Nob29sIHllYXI6CmBgYHtyfQpnZ3Bsb3QodHJpYWxzX2J5X3dkYXlfaG91cltjb3Vyc2UgJWluJSBjKCJFbmdsaXNoIiwgIkZyZW5jaCIpXSwKICAgICAgIGFlcyh4ID0gaG91ciwgeSA9IHJlb3JkZXIod2Vla2RheSwgZHBseXI6OmRlc2Mod2Vla2RheSkpLCBmaWxsID0gdHJpYWxzX25vcm1hbGlzZWRfc2Nob29seWVhcikpICsgCiAgZmFjZXRfZ3JpZChzY2hvb2xfeWVhciB+IGNvdXJzZSkgKwogIGdlb21fdGlsZShjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gMC4yNSkgKwogIGxhYnMoeCA9ICJUaW1lIG9mIGRheSAoaG91cikiLAogICAgICAgeSA9IE5VTEwpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApLCBicmVha3MgPSBzZXEoMCwgMjQsIDMpKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQgPSBjKDAsMCkpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gIkEiLCBkaXJlY3Rpb24gPSAtMSkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIGd1aWRlcyhmaWxsID0gRkFMU0UpICsKICB0aGVtZV9wYXBlcgpgYGAKClBsb3QgaGVhdG1hcCBwZXIgcXVhcnRlcjoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTZ9CmdncGxvdCh0cmlhbHNfYnlfd2RheV9ob3VyLCBhZXMoeCA9IGhvdXIsIHkgPSByZW9yZGVyKHdlZWtkYXksIGRwbHlyOjpkZXNjKHdlZWtkYXkpKSwgZmlsbCA9IHRyaWFsc19ub3JtYWxpc2VkX3F1YXJ0ZXIpKSArIAogIGZhY2V0X2dyaWQocXVhcnRlciB+IG1ldGhvZCkgKwogIGdlb21fdGlsZShjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gMC4yNSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gTlVMTCwKICAgICAgIHRpdGxlID0gIkFjdGl2aXRlaXQgcGVyIHV1ciBnZWR1cmVuZGUgZGUgd2VlayIsCiAgICAgICBjYXB0aW9uID0gIkFhbnRhbCB0cmlhbHMgcGVyIHdlZWtkYWcgZW4gdXVyIGluIGVsayBrd2FydGFhbCwgZ2Vub3JtYWxpc2VlcmQgcGVyIG1ldGhvZGUuIikgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsMCksIGJyZWFrcyA9IHNlcSgwLCAyNCwgMykpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwwKSkgKyAKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAiQSIsIGRpcmVjdGlvbiA9IC0xKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KQpgYGAKClBsb3QgaGVhdG1hcCBmb3IgdGhlIHBlcmlvZCBpbiB3aGljaCBzY2hvb2xzIHdlcmUgY2xvc2VkOgpgYGB7cn0KdHJpYWxzX2Nsb3NlZCA8LSB1bmlxdWUodHJpYWxzX2J5X3dkYXlfaG91cltzY2hvb2xzX2Nsb3NlZCA9PSBUUlVFLCAuKGNvdXJzZSwgc2Nob29sX3llYXIsIHdlZWtkYXksIGhvdXIsIHRyaWFsc19jbG9zZWQpXSkKCnRyaWFsc19jbG9zZWRbLCB0cmlhbHNfbm9ybWFsaXNlZF9jbG9zZWQgOj0gdHJpYWxzX2Nsb3NlZCAvIHN1bSh0cmlhbHNfY2xvc2VkKSwgYnkgPSAuKGNvdXJzZSwgc2Nob29sX3llYXIpXQpgYGAKCmBgYHtyfQp0cmlhbHNfY2xvc2VkX2RpZmYgPC0gdHJpYWxzX2Nsb3NlZFssIC4oc2Nob29sX3llYXIgPSAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyaWFsc19jbG9zZWQgPSB0cmlhbHNfY2xvc2VkW3NjaG9vbF95ZWFyID09ICIxOS8yMCJdIC0gdHJpYWxzX2Nsb3NlZFtzY2hvb2xfeWVhciA9PSAiMTgvMTkiXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyaWFsc19ub3JtYWxpc2VkX2Nsb3NlZCA9IHRyaWFsc19ub3JtYWxpc2VkX2Nsb3NlZFtzY2hvb2xfeWVhciA9PSAiMTkvMjAiXSAtIHRyaWFsc19ub3JtYWxpc2VkX2Nsb3NlZFtzY2hvb2xfeWVhciA9PSAiMTgvMTkiXSksIGJ5ID0gLihjb3Vyc2UsIHdlZWtkYXksIGhvdXIpXQpgYGAKCgpgYGB7cn0KcF9oZWF0bWFwIDwtIGdncGxvdCh0cmlhbHNfY2xvc2VkW2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIiksXSwKICAgICAgIGFlcyh4ID0gaG91ciwgeSA9IHJlb3JkZXIod2Vla2RheSwgZHBseXI6OmRlc2Mod2Vla2RheSkpLCBmaWxsID0gdHJpYWxzX25vcm1hbGlzZWRfY2xvc2VkKSkgKyAKICBmYWNldF9ncmlkKHNjaG9vbF95ZWFyIH4gY291cnNlKSArCiAgZ2VvbV90aWxlKGNvbG91ciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjI1KSArCiAgbGFicyh4ID0gIlRpbWUgb2YgZGF5IChob3VyKSIsCiAgICAgICB5ID0gTlVMTCwKICAgICAgIGZpbGwgPSBOVUxMKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSwgYnJlYWtzID0gc2VxKDAsIDI0LCAzKSkgKwogIHNjYWxlX3lfZGlzY3JldGUoZXhwYW5kID0gYygwLDApKSArIAogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJBIiwgZGlyZWN0aW9uID0gLTEpICsKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9wYXBlcgoKCnBfaGVhdG1hcApgYGAKCgpNYWtlIGEgcGxvdCBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gc2Nob29sIHllYXJzIGR1cmluZyB0aGUgc2Nob29sIGNsb3N1cmUgcGVyaW9kOgpgYGB7cn0KcF9oZWF0bWFwX2RpZmYgPC0gZ2dwbG90KHRyaWFsc19jbG9zZWRfZGlmZltjb3Vyc2UgJWluJSBjKCJFbmdsaXNoIiwgIkZyZW5jaCIpLF0sCiAgICAgICBhZXMoeCA9IGhvdXIsIHkgPSByZW9yZGVyKHdlZWtkYXksIGRwbHlyOjpkZXNjKHdlZWtkYXkpKSwgZmlsbCA9IHRyaWFsc19ub3JtYWxpc2VkX2Nsb3NlZCkpICsgCiAgZmFjZXRfZ3JpZChzY2hvb2xfeWVhciB+IGNvdXJzZSkgKwogIGdlb21fdGlsZShjb2xvdXIgPSAid2hpdGUiLCBzaXplID0gMC4yNSkgKwogIGxhYnMoeCA9ICJUaW1lIG9mIGRheSAoaG91cikiLAogICAgICAgeSA9IE5VTEwsCiAgICAgICBmaWxsID0gTlVMTCkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsMCksIGJyZWFrcyA9IHNlcSgwLCAyNCwgMykpICsKICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwwKSkgKyAKICBzY2FsZV9maWxsX2Rpc3RpbGxlcih0eXBlID0gImRpdiIsIHBhbGV0dGUgPSAiUmRCdSIsIGRpcmVjdGlvbiA9IC0xLCBsaW1pdHMgPSBjKC0xLCAxKSAqIG1heChhYnModHJpYWxzX2Nsb3NlZF9kaWZmW2NvdXJzZSAlaW4lIGMoIkVuZ2xpc2giLCAiRnJlbmNoIiksXSR0cmlhbHNfbm9ybWFsaXNlZF9jbG9zZWQpKSkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX3BhcGVyCgpwX2hlYXRtYXBfZGlmZgpgYGAKCgpNYWtlIGEgY29tYmluZWQgcGxvdCBmb3IgaW4gdGhlIHBhcGVyOgpgYGB7cn0KcF9oZWF0bWFwX2xlZ2VuZCA8LSBnZXRfbGVnZW5kKHBfaGVhdG1hcCkKcF9oZWF0bWFwX2RpZmZfbGVnZW5kIDwtIGdldF9sZWdlbmQocF9oZWF0bWFwX2RpZmYpCgpwX2hlYXRtYXAgPC0gcF9oZWF0bWFwICsgZ3VpZGVzKGZpbGwgPSBGQUxTRSkKcF9oZWF0bWFwX2RpZmYgPC0gcF9oZWF0bWFwX2RpZmYgKyBndWlkZXMoZmlsbCA9IEZBTFNFKQpgYGAKCmBgYHtyfQpwbG90X2dyaWQoCiAgcGxvdF9ncmlkKHBfaGVhdG1hcCwgcF9oZWF0bWFwX2RpZmYsCiAgICAgICAgICBuY29sID0gMSwKICAgICAgICAgIGxhYmVscyA9IGMoIkEiLCAiQiIpLAogICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDEsIC42NTUpCiAgICAgICAgICApLAogIHBsb3RfZ3JpZChwX2hlYXRtYXBfbGVnZW5kLCBwX2hlYXRtYXBfZGlmZl9sZWdlbmQsCiAgICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgICBhbGlnbiA9ICJ2aCIsIGF4aXMgPSAibHJ0YiIpLAogIG5jb2wgPSAyLAogIHJlbF93aWR0aHMgPSBjKDEsIC4xNSkpCgpnZ3NhdmUoIi4uL291dHB1dC9jb21iaV9oZWF0bWFwLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNC41KQpnZ3NhdmUoIi4uL291dHB1dC9jb21iaV9oZWF0bWFwLnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNC41KQpgYGAKCgojIyBBY3Rpdml0eSBzdHJhdGlmaWVkIGJ5IHllYXIgYW5kIGxldmVsCgpgYGB7cn0KZGIgPC0gZGJfY29ubmVjdCgpCmNvdW50c19zdHJhdCA8LSBkYkdldFF1ZXJ5KGRiLCJTRUxFQ1QgcmVzcG9uc2VzLm1ldGhvZCBBUyAnbWV0aG9kJywKICAgICAgICAgICAgICAgICAgICAgICAgICByZXNwb25zZXMuYm9va19pbmZvX2lkIGFzICdib29rX2luZm9faWQnLAogICAgICAgICAgICAgICAgICAgICAgICAgIERBVEUocmVzcG9uc2VzLmRhdGUgKyAzNjAwLCAndW5peGVwb2NoJykgQVMgJ2RveScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzcG9uc2VzLnVzZXJfaWQgQVMgJ3VzZXInLAogICAgICAgICAgICAgICAgICAgICAgICAgIENPVU5UKCopIEFTICd0cmlhbHMnCiAgICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSAncmVzcG9uc2VzJwogICAgICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIHJlc3BvbnNlcy5tZXRob2QsIHJlc3BvbnNlcy5ib29rX2luZm9faWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgREFURShyZXNwb25zZXMuZGF0ZSArIDM2MDAsICd1bml4ZXBvY2gnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICByZXNwb25zZXMudXNlcl9pZAogICAgICAgICAgICAgICAgICAgICAgICAiKQpkYl9kaXNjb25uZWN0KGRiKQoKc2V0RFQoY291bnRzX3N0cmF0KQpgYGAKCmBgYHtyfQpkYiA8LSBkYl9jb25uZWN0KCkKYm9va19pbmZvIDwtIGRiR2V0UXVlcnkoZGIsICJTRUxFQ1QgKiBGUk9NICdib29rX2luZm8nIikKZGJfZGlzY29ubmVjdChkYikKCnNldERUKGJvb2tfaW5mbykKYGBgCgpBZGQgYm9vayBpbmZvcm1hdGlvbjoKYGBge3J9CmNvdW50c19zdHJhdFtib29rX2luZm8sIG9uID0gImJvb2tfaW5mb19pZCIsIGMoImJvb2tfdGl0bGUiLCAibWV0aG9kX2dyb3VwIikgOj0gLihpLmJvb2tfdGl0bGUsIGkubWV0aG9kX2dyb3VwKV0KYGBgCgpBZGQgYSBzY2hvb2wgeWVhciBjb2x1bW4gKGN1dG9mZiBkYXRlOiAxIEF1Z3VzdCk6CmBgYHtyfQpjb3VudHNfc3RyYXRbLCBkb3lfcG9zaXggOj0gYXMuUE9TSVhjdChkb3kpXQpjb3VudHNfc3RyYXRbLCBzY2hvb2xfeWVhciA6PSBpZmVsc2UoZG95X3Bvc2l4IDwgIjIwMTktMDgtMDEiLCAiMTgvMTkiLCAiMTkvMjAiKV0KYGBgCgpBZGQgc2Vuc2libGUgY291cnNlIG5hbWVzOgpgYGB7cn0KY291bnRzX3N0cmF0WywgY291cnNlIDo9IGlmZWxzZShtZXRob2QgPT0gIkdyYW5kZXMgTGlnbmVzIiwgIkZyZW5jaCIsIGlmZWxzZShtZXRob2QgPT0gIlN0ZXBwaW5nIFN0b25lcyIsICJFbmdsaXNoIiwgIkdlcm1hbiIpKV0KYGBgCgpDb3VudCB0cmlhbHMgYnkgZGF5OgpgYGB7cn0KY291bnRzX3N0cmF0X2J5X2RheSA8LSBjb3VudHNfc3RyYXRbLCAuKHRyaWFsc190b3RhbCA9IHN1bSh0cmlhbHMsIG5hLnJtID0gVFJVRSkpLCBieSA9IC4oc2Nob29sX3llYXIsIGNvdXJzZSwgbWV0aG9kX2dyb3VwLCBib29rX3RpdGxlLCBkb3lfcG9zaXgpXQpzZXRvcmRlcihjb3VudHNfc3RyYXRfYnlfZGF5LCBzY2hvb2xfeWVhciwgY291cnNlLCBtZXRob2RfZ3JvdXAsIGJvb2tfdGl0bGUsIGRveV9wb3NpeCkKYGBgCgpTaW1wbGlmeSBsZXZlbCBuYW1lczoKYGBge3J9CiMgS2VlcCBhbGwgZGlzdGluY3Rpb25zCmNvdW50c19zdHJhdF9ieV9kYXlbLCBib29rX3RpdGxlX3NpbXBsZSA6PSBzdHJpbmdyOjpzdHJfc3ViKGJvb2tfdGl0bGUsIDMsIC0xMCldCmNvdW50c19zdHJhdF9ieV9kYXlbLCBib29rX3RpdGxlX3NpbXBsZSA6PSBmYWN0b3IoYm9va190aXRsZV9zaW1wbGUsIGxldmVscyA9IGMoInZtYm8gYi9sd29vIiwgInZtYm8gYiIsICJ2bWJvIGJrIiwgInZtYm8gayIsICJ2bWJvIGtndCIsICJ2bWJvLWd0IiwgInZtYm8gZ3QiLCAidm1iby1ndC9oYXZvIiwgInZtYm8gKHQpaHYiLCAiaGF2byIsICJoYXZvIHZ3byIsICJ2d28iKSldCgojIFNpbXBsaWZ5IHRvIHRocmVlIGxldmVscwpjb3VudHNfc3RyYXRfYnlfZGF5WywgbGV2ZWwgOj0gZHBseXI6OmNhc2Vfd2hlbigKICBncmVwbCggImh2IiwgYm9va190aXRsZSkgfiAiR2VuZXJhbCBzZWNvbmRhcnlcbihoYXZvKSIsCiAgZ3JlcGwoInZtYm8iLCBib29rX3RpdGxlKSB+ICJQcmUtdm9jYXRpb25hbFxuKHZtYm8pIiwKICBncmVwbCgiaGF2byIsIGJvb2tfdGl0bGUpIH4gIkdlbmVyYWwgc2Vjb25kYXJ5XG4oaGF2bykiLAogIGdyZXBsKCJ2d28iLCBib29rX3RpdGxlKSB+ICJQcmUtdW5pdmVyc2l0eVxuKHZ3bykiLAogIFRSVUUgfiAiT3RoZXIiKV0KY291bnRzX3N0cmF0X2J5X2RheVssIGxldmVsIDo9IGZhY3RvcihsZXZlbCwgbGV2ZWxzID0gYygiT3RoZXIiLCAiUHJlLXZvY2F0aW9uYWxcbih2bWJvKSIsICJHZW5lcmFsIHNlY29uZGFyeVxuKGhhdm8pIiwgIlByZS11bml2ZXJzaXR5XG4odndvKSIpKV0KYGBgCgpTaW1wbGlmeSB5ZWFyIG5hbWVzOgpgYGB7cn0KY291bnRzX3N0cmF0X2J5X2RheVssIHllYXIgOj0gZHBseXI6OmNhc2Vfd2hlbigKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDEgKDVlIEVkLikiIH4gIlllYXIgMSIsCiAgbWV0aG9kX2dyb3VwID09ICJMZWVyamFhciAyICg1ZSBFZC4pIiB+ICJZZWFyIDIiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgMyAoNWUgRWQuKSIgfiAiWWVhciAzIiwKICBtZXRob2RfZ3JvdXAgPT0gIkxlZXJqYWFyIDMvNCAoNWUgRWQuKSIgfiAiWWVhciAzLzQiLAogIG1ldGhvZF9ncm91cCA9PSAiTGVlcmphYXIgNCAoNWUgRWQuKSIgfiAiWWVhciA0IiwKICBtZXRob2RfZ3JvdXAgPT0gIlR3ZWVkZSBGYXNlICg2ZSBFZC4pIiB+ICJUd2VlZGUgRmFzZSIsCiAgVFJVRSB+ICJPdGhlciIpXQpgYGAKCgpBbGlnbiBzY2hvb2wgeWVhcnM6CmBgYHtyfQpjb3VudHNfc3RyYXRfYnlfZGF5W3NjaG9vbF95ZWFyID09ICIxOC8xOSIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGFzLlBPU0lYY3QoZG95X3Bvc2l4ICsgMzY1KjI0KjYwKjYwLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpXQpjb3VudHNfc3RyYXRfYnlfZGF5W3NjaG9vbF95ZWFyID09ICIxOS8yMCIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGRveV9wb3NpeF0KYGBgCgpVc2UgY3V0LkRhdGUoKSB0byBiaW4gZGF0ZXMgYnkgd2Vlay4gRWFjaCBkYXkgaXMgYXNzaWduZWQgdGhlIGRhdGUgb2YgdGhlIG1vc3QgcmVjZW50IE1vbmRheS4KYGBge3J9CmNvdW50c19zdHJhdF9ieV9kYXlbLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrIDo9IGN1dC5QT1NJWHQoZG95X3Bvc2l4X2FsaWduZWQsICJ3ZWVrIildCmNvdW50c19zdHJhdF9ieV9kYXlbLCB0cmlhbHNfdG90YWxfd2VlayA6PSBzdW0odHJpYWxzX3RvdGFsLCBuYS5ybSA9IFRSVUUpLCBieSA9IC4oc2Nob29sX3llYXIsIGNvdXJzZSwgbWV0aG9kX2dyb3VwLCBib29rX3RpdGxlX3NpbXBsZSwgZG95X3Bvc2l4X2FsaWduZWRfd2VlayldCgpjb3VudHNfc3RyYXRfYnlfZGF5WywgdHJpYWxzX3RvdGFsX3dlZWtfbGV2ZWwgOj0gc3VtKHRyaWFsc190b3RhbCksIGJ5ID0gLihzY2hvb2xfeWVhciwgY291cnNlLCBtZXRob2RfZ3JvdXAsIGxldmVsLCBkb3lfcG9zaXhfYWxpZ25lZF93ZWVrKV0KYGBgCgoKU3VtbWFyaXNlIGluY3JlYXNlIGR1cmluZyBsb2NrZG93bjoKYGBge3J9CmNvdW50c19zdHJhdF9pbmNyZWFzZSA8LSBjb3VudHNfc3RyYXRfYnlfZGF5W2JldHdlZW4oZG95X3Bvc2l4X2FsaWduZWQsIGRhdGVfc2Nob29sc19jbG9zZWQsIGRhdGVfc2Nob29sc19vcGVuZWQpLCAuKHRyaWFsc19sb2NrZG93biA9IHN1bSh0cmlhbHNfdG90YWwpKSwgYnkgPSAuKGNvdXJzZSwgYm9va190aXRsZV9zaW1wbGUsIG1ldGhvZF9ncm91cCwgeWVhciwgc2Nob29sX3llYXIpXQpjb3VudHNfc3RyYXRfaW5jcmVhc2VbLCBpbmNyZWFzZSA6PSB0cmlhbHNfbG9ja2Rvd25bMl0vdHJpYWxzX2xvY2tkb3duWzFdLCBieSA9IC4oY291cnNlLCBib29rX3RpdGxlX3NpbXBsZSwgbWV0aG9kX2dyb3VwLCB5ZWFyKV0KY291bnRzX3N0cmF0X2luY3JlYXNlWywgaW5jcmVhc2VfcGN0IDo9IHBhc3RlMCgiQ2hhbmdlOlxuIiwgc2NhbGVzOjpwZXJjZW50KGluY3JlYXNlLCBhY2N1cmFjeSA9IDIpKV0KCmNvdW50c19zdHJhdF9pbmNyZWFzZV9sZXZlbCA8LSBjb3VudHNfc3RyYXRfYnlfZGF5W2JldHdlZW4oZG95X3Bvc2l4X2FsaWduZWQsIGRhdGVfc2Nob29sc19jbG9zZWQsIGRhdGVfc2Nob29sc19vcGVuZWQpLCAuKHRyaWFsc19sb2NrZG93biA9IHN1bSh0cmlhbHNfdG90YWwpKSwgYnkgPSAuKGNvdXJzZSwgbGV2ZWwsIG1ldGhvZF9ncm91cCwgeWVhciwgc2Nob29sX3llYXIpXQpjb3VudHNfc3RyYXRfaW5jcmVhc2VfbGV2ZWxbLCBpbmNyZWFzZSA6PSB0cmlhbHNfbG9ja2Rvd25bMl0vdHJpYWxzX2xvY2tkb3duWzFdLCBieSA9IC4oY291cnNlLCBsZXZlbCwgbWV0aG9kX2dyb3VwLCB5ZWFyKV0KY291bnRzX3N0cmF0X2luY3JlYXNlX2xldmVsWywgaW5jcmVhc2VfcGN0IDo9IHBhc3RlMCgiQ2hhbmdlOlxuIiwgc2NhbGVzOjpwZXJjZW50KGluY3JlYXNlLCBhY2N1cmFjeSA9IDIpKV0KYGBgCgoKIyMjIEZyZW5jaApgYGB7cn0KZ2dwbG90KGNvdW50c19zdHJhdF9ieV9kYXlbY291cnNlID09ICJGcmVuY2giXSwgCiAgICAgICBhZXMoZ3JvdXAgPSBzY2hvb2xfeWVhciwgY29sb3VyID0gc2Nob29sX3llYXIsIGZpbGwgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKGJvb2tfdGl0bGVfc2ltcGxlIH4gbWV0aG9kX2dyb3VwKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0yZTUsIHltYXggPSAyLjJlNiwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMikgKwogIGdlb21fcmliYm9uKGFlcyh4ID0gZG95X3Bvc2l4X2FsaWduZWQsIHltaW4gPSAwLCB5bWF4ID0gdHJpYWxzX3RvdGFsX3dlZWssICksIGFscGhhID0gLjIpICsKICBnZW9tX3RleHQoZGF0YSA9IGNvdW50c19zdHJhdF9pbmNyZWFzZVtjb3Vyc2UgPT0gIkZyZW5jaCIgJiBzY2hvb2xfeWVhciA9PSAiMTkvMjAiXSwgCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IGluY3JlYXNlX3BjdCksCiAgICAgICAgICAgIHggPSBhcy5QT1NJWGN0KChhcy5udW1lcmljKGRhdGVfc2Nob29sc19jbG9zZWQpICsgYXMubnVtZXJpYyhkYXRlX3NjaG9vbHNfb3BlbmVkKSkvMiwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKSwKICAgICAgICAgICAgeSA9IDMuNmU1LAogICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICB2anVzdCA9IDEsCiAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMy0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMy43NWU1KSwgbGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJUcmlhbHMgcGVyIHdlZWsiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiLAogICAgICAgdGl0bGUgPSAiRnJlbmNoIikgKwogIHRoZW1lX3BhcGVyCgpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0X2ZyZW5jaC5wZGYiLCB3aWR0aCA9IDE0LCBoZWlnaHQgPSAxMCkKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdF9mcmVuY2gucG5nIiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTApCmBgYAoKU3RyZWFtbGluZWQgdmVyc2lvbiBmb3IgaW4gdGhlIHBhcGVyOgpgYGB7cn0KZ2dwbG90KGNvdW50c19zdHJhdF9ieV9kYXlbY291cnNlID09ICJGcmVuY2giXSwgCiAgICAgICBhZXMoZ3JvdXAgPSBzY2hvb2xfeWVhciwgY29sb3VyID0gc2Nob29sX3llYXIsIGZpbGwgPSBzY2hvb2xfeWVhcikpICsKICBmYWNldF9ncmlkKGxldmVsIH4geWVhcikgKwogIGdlb21fcmVjdCh4bWluID0gZGF0ZV9zY2hvb2xzX2Nsb3NlZCwgeG1heCA9IGRhdGVfc2Nob29sc19vcGVuZWQsIHltaW4gPSAtMmU1LCB5bWF4ID0gMi4yZTYsIGZpbGwgPSAiZ3JleTkyIiwgY29sb3VyID0gImdyZXk1MCIsIGx0eSA9IDIpICsKICBnZW9tX3JpYmJvbihhZXMoeCA9IGRveV9wb3NpeF9hbGlnbmVkLCB5bWluID0gMCwgeW1heCA9IHRyaWFsc190b3RhbF93ZWVrX2xldmVsLCApLCBhbHBoYSA9IC4yKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBjb3VudHNfc3RyYXRfaW5jcmVhc2VfbGV2ZWxbY291cnNlID09ICJGcmVuY2giICYgc2Nob29sX3llYXIgPT0gIjE5LzIwIl0sIAogICAgICAgICAgICBhZXMobGFiZWwgPSBpbmNyZWFzZV9wY3QpLAogICAgICAgICAgICB4ID0gYXMuUE9TSVhjdCgoYXMubnVtZXJpYyhkYXRlX3NjaG9vbHNfY2xvc2VkKSArIGFzLm51bWVyaWMoZGF0ZV9zY2hvb2xzX29wZW5lZCkpLzIsIG9yaWdpbiA9ICIxOTcwLTAxLTAxIiksCiAgICAgICAgICAgIHkgPSAzLjZlNSwKICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwKICAgICAgICAgICAgdmp1c3QgPSAxLAogICAgICAgICAgICBzaXplID0gcmVsKDIuNzUpLAogICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfeF9kYXRldGltZShleHBhbmQgPSBjKDAsIDApLCAKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTAtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDQtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDYtMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIDMuNzVlNSksIGxhYmVscyA9IG51bWJlcl9mb3JtYXQpICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwKICAgICAgIHkgPSAiVHJpYWxzIHBlciB3ZWVrIiwKICAgICAgIGNvbG91ciA9ICJTY2hvb2wgeWVhciIsCiAgICAgICBmaWxsID0gIlNjaG9vbCB5ZWFyIikgKwogIHRoZW1lX3BhcGVyCgpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0X2ZyZW5jaF9sZXZlbC5wZGYiLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpCmdnc2F2ZSgiLi4vb3V0cHV0L3RyaWFsX2hpc3RfZnJlbmNoX2xldmVsLnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSkKYGBgCgojIyMgRW5nbGlzaAoKYGBge3J9CmdncGxvdChjb3VudHNfc3RyYXRfYnlfZGF5W2NvdXJzZSA9PSAiRW5nbGlzaCJdLCAKICAgICAgIGFlcyhncm91cCA9IHNjaG9vbF95ZWFyLCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgZmlsbCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQoYm9va190aXRsZV9zaW1wbGUgfiBtZXRob2RfZ3JvdXApICsKICBnZW9tX3JlY3QoeG1pbiA9IGRhdGVfc2Nob29sc19jbG9zZWQsIHhtYXggPSBkYXRlX3NjaG9vbHNfb3BlbmVkLCB5bWluID0gLTJlNSwgeW1heCA9IDIuMmU2LCBmaWxsID0gImdyZXk5MiIsIGNvbG91ciA9ICJncmV5NTAiLCBsdHkgPSAyKSArCiAgZ2VvbV9yaWJib24oYWVzKHggPSBkb3lfcG9zaXhfYWxpZ25lZCwgeW1pbiA9IDAsIHltYXggPSB0cmlhbHNfdG90YWxfd2VlaywgKSwgYWxwaGEgPSAuMikgKwogIGdlb21fdGV4dChkYXRhID0gY291bnRzX3N0cmF0X2luY3JlYXNlW2NvdXJzZSA9PSAiRW5nbGlzaCIgJiBzY2hvb2xfeWVhciA9PSAiMTkvMjAiXSwgCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IGluY3JlYXNlX3BjdCksCiAgICAgICAgICAgIHggPSBhcy5QT1NJWGN0KChhcy5udW1lcmljKGRhdGVfc2Nob29sc19jbG9zZWQpICsgYXMubnVtZXJpYyhkYXRlX3NjaG9vbHNfb3BlbmVkKSkvMiwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKSwKICAgICAgICAgICAgeSA9IDMuNmU1LAogICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICB2anVzdCA9IDEsCiAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV94X2RhdGV0aW1lKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMuUE9TSVhjdChjKAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAxOS0xMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wMy0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNS0wMSAwMjowMDowMCBDRVQiLAogICAgICAgICAgICAgICAgICAgICAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5QT1NJWGN0KGMoIjIwMTktMDktMDEgMDI6MDA6MDAgQ0VUIiwgIjIwMjAtMDctMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgMy43NWU1KSwgbGFiZWxzID0gbnVtYmVyX2Zvcm1hdCkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGVuZCA9IC41LCBkaXJlY3Rpb24gPSAtMSwgbmEudHJhbnNsYXRlID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLAogICAgICAgeSA9ICJUcmlhbHMgcGVyIHdlZWsiLAogICAgICAgY29sb3VyID0gIlNjaG9vbCB5ZWFyIiwKICAgICAgIGZpbGwgPSAiU2Nob29sIHllYXIiLAogICAgICAgdGl0bGUgPSAiRW5nbGlzaCIpICsKICB0aGVtZV9wYXBlcgoKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdF9lbmdsaXNoLnBkZiIsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEwKQpnZ3NhdmUoIi4uL291dHB1dC90cmlhbF9oaXN0X2VuZ2xpc2gucG5nIiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTApCmBgYAoKU3RyZWFtbGluZWQgdmVyc2lvbiBmb3IgaW4gdGhlIHBhcGVyOgpgYGB7cn0KZ2dwbG90KGNvdW50c19zdHJhdF9ieV9kYXlbY291cnNlID09ICJFbmdsaXNoIiAmIGxldmVsICE9ICJPdGhlciJdLCAKICAgICAgIGFlcyhncm91cCA9IHNjaG9vbF95ZWFyLCBjb2xvdXIgPSBzY2hvb2xfeWVhciwgZmlsbCA9IHNjaG9vbF95ZWFyKSkgKwogIGZhY2V0X2dyaWQobGV2ZWwgfiB5ZWFyKSArCiAgZ2VvbV9yZWN0KHhtaW4gPSBkYXRlX3NjaG9vbHNfY2xvc2VkLCB4bWF4ID0gZGF0ZV9zY2hvb2xzX29wZW5lZCwgeW1pbiA9IC0yZTUsIHltYXggPSAyLjJlNiwgZmlsbCA9ICJncmV5OTIiLCBjb2xvdXIgPSAiZ3JleTUwIiwgbHR5ID0gMikgKwogIGdlb21fcmliYm9uKGFlcyh4ID0gZG95X3Bvc2l4X2FsaWduZWQsIHltaW4gPSAwLCB5bWF4ID0gdHJpYWxzX3RvdGFsX3dlZWtfbGV2ZWwsICksIGFscGhhID0gLjIpICsKICBnZW9tX3RleHQoZGF0YSA9IGNvdW50c19zdHJhdF9pbmNyZWFzZV9sZXZlbFtjb3Vyc2UgPT0gIkVuZ2xpc2giICYgbGV2ZWwgIT0gIk90aGVyIiAmIHNjaG9vbF95ZWFyID09ICIxOS8yMCJdLCAKICAgICAgICAgICAgYWVzKGxhYmVsID0gaW5jcmVhc2VfcGN0KSwKICAgICAgICAgICAgeCA9IGFzLlBPU0lYY3QoKGFzLm51bWVyaWMoZGF0ZV9zY2hvb2xzX2Nsb3NlZCkgKyBhcy5udW1lcmljKGRhdGVfc2Nob29sc19vcGVuZWQpKS8yLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpLAogICAgICAgICAgICB5ID0gOS42ZTUsCiAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsCiAgICAgICAgICAgIHZqdXN0ID0gMSwKICAgICAgICAgICAgc2l6ZSA9IHJlbCgyLjc1KSwKICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICBicmVha3MgPSBhcy5QT1NJWGN0KGMoCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEwLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDE5LTEyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTAyLTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA0LTAxIDAyOjAwOjAwIENFVCIsCiAgICAgICAgICAgICAgICAgICAgICIyMDIwLTA2LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGFzLlBPU0lYY3QoYygiMjAxOS0wOS0wMSAwMjowMDowMCBDRVQiLCAiMjAyMC0wNy0wMSAwMjowMDowMCBDRVQiKSksCiAgICAgICAgICAgICAgICAgICBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgbGltaXRzID0gYygwLCAxZTYpLCBsYWJlbHMgPSBudW1iZXJfZm9ybWF0KSArCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChlbmQgPSAuNSwgZGlyZWN0aW9uID0gLTEsIG5hLnRyYW5zbGF0ZSA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZW5kID0gLjUsIGRpcmVjdGlvbiA9IC0xLCBuYS50cmFuc2xhdGUgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlRyaWFscyBwZXIgd2VlayIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiLAogICAgICAgZmlsbCA9ICJTY2hvb2wgeWVhciIpICsKICB0aGVtZV9wYXBlcgoKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdF9lbmdsaXNoX2xldmVsLnBkZiIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSkKZ2dzYXZlKCIuLi9vdXRwdXQvdHJpYWxfaGlzdF9lbmdsaXNoX2xldmVsLnBuZyIsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSkKYGBgCgoKCiMjIFF1ZXN0aW9uIHR5cGUKClRoZXJlIGFyZSBkaWZmZXJlbnQgcXVlc3Rpb24gZm9ybWF0czogb3Blbi1hbnN3ZXIsIGluIHdoaWNoIHRoZSBzdHVkZW50IHR5cGVzIHRoZSBhbnN3ZXIsIGFuZCBtdWx0aXBsZS1jaG9pY2UsIGluIHdoaWNoIHRoZSBzdHVkZW50IHNlbGVjdHMgdGhlIGFuc3dlciBmcm9tIGEgc2V0IG9mIDMgb3IgNCBvcHRpb25zLgoKYGBge3J9CmRiIDwtIGRiX2Nvbm5lY3QoKQpxdWVzdGlvbl90eXBlIDwtIGRiR2V0UXVlcnkoZGIsIAogICAgICAgICAgICAgICAgICAgICAgIlNFTEVDVCByLm1ldGhvZCBBUyAnbWV0aG9kJywKICAgICAgICAgICAgICAgICAgICAgIERBVEUoci5kYXRlICsgMzYwMCwgJ3VuaXhlcG9jaCcpIEFTICdkb3knLAogICAgICAgICAgICAgICAgICAgICAgci5jaG9pY2VzIEFTICdjaG9pY2VzJywKICAgICAgICAgICAgICAgICAgICAgIENPVU5UKCopIEFTICduJwogICAgICAgICAgICAgICAgICAgICAgRlJPTSAncmVzcG9uc2VzJyByCiAgICAgICAgICAgICAgICAgICAgICBXSEVSRSByLnN0dWR5ID09IDAKICAgICAgICAgICAgICAgICAgICAgIEdST1VQIEJZIHIubWV0aG9kLAogICAgICAgICAgICAgICAgICAgICAgREFURShyLmRhdGUgKyAzNjAwLCAndW5peGVwb2NoJyksCiAgICAgICAgICAgICAgICAgICAgICByLmNob2ljZXMiCikKc2V0RFQocXVlc3Rpb25fdHlwZSkKZGJfZGlzY29ubmVjdChkYikKYGBgCgpBZGQgYSBzY2hvb2wgeWVhciBjb2x1bW4gKGN1dG9mZiBkYXRlOiAxIEF1Z3VzdCk6CmBgYHtyfQpxdWVzdGlvbl90eXBlWywgZG95X3Bvc2l4IDo9IGFzLlBPU0lYY3QoZG95KV0KcXVlc3Rpb25fdHlwZVssIHNjaG9vbF95ZWFyIDo9IGlmZWxzZShkb3lfcG9zaXggPCAiMjAxOS0wOC0wMSIsICIxOC8xOSIsICIxOS8yMCIpXQpgYGAKCkFkZCBzZW5zaWJsZSBjb3Vyc2UgbmFtZXM6CmBgYHtyfQpxdWVzdGlvbl90eXBlWywgY291cnNlIDo9IGlmZWxzZShtZXRob2QgPT0gIkdyYW5kZXMgTGlnbmVzIiwgIkZyZW5jaCIsIGlmZWxzZShtZXRob2QgPT0gIlN0ZXBwaW5nIFN0b25lcyIsICJFbmdsaXNoIiwgIkdlcm1hbiIpKV0KYGBgCgpBbGlnbiBzY2hvb2wgeWVhcnM6CmBgYHtyfQpxdWVzdGlvbl90eXBlW3NjaG9vbF95ZWFyID09ICIxOC8xOSIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGFzLlBPU0lYY3QoZG95X3Bvc2l4ICsgMzY1KjI0KjYwKjYwLCBvcmlnaW4gPSAiMTk3MC0wMS0wMSIpXQpxdWVzdGlvbl90eXBlW3NjaG9vbF95ZWFyID09ICIxOS8yMCIsIGRveV9wb3NpeF9hbGlnbmVkIDo9IGRveV9wb3NpeF0KYGBgCgpVc2UgY3V0LkRhdGUoKSB0byBiaW4gZGF0ZXMgYnkgd2Vlay4gRWFjaCBkYXkgaXMgYXNzaWduZWQgdGhlIGRhdGUgb2YgdGhlIG1vc3QgcmVjZW50IE1vbmRheS4KYGBge3J9CnF1ZXN0aW9uX3R5cGVbLCBkb3lfcG9zaXhfd2VlayA6PSBjdXQuUE9TSVh0KGRveV9wb3NpeCwgIndlZWsiKV0KcXVlc3Rpb25fdHlwZVssIGRveV9wb3NpeF9hbGlnbmVkX3dlZWsgOj0gY3V0LlBPU0lYdChkb3lfcG9zaXhfYWxpZ25lZCwgIndlZWsiKV0KYGBgCgpgYGB7cn0KcXVlc3Rpb25fdHlwZV9ieV93ZWVrIDwtIHF1ZXN0aW9uX3R5cGVbLCAuKG4gPSBzdW0obikpLCBieSA9IC4oY291cnNlLCBzY2hvb2xfeWVhciwgZG95X3Bvc2l4X2FsaWduZWRfd2VlaywgY2hvaWNlcyldCmBgYAoKYGBge3J9CmdncGxvdChxdWVzdGlvbl90eXBlX2J5X3dlZWtbY291cnNlICVpbiUgYygiRW5nbGlzaCIsICJGcmVuY2giKV0sIGFlcyh4ID0gYXMuUE9TSVhjdChkb3lfcG9zaXhfYWxpZ25lZF93ZWVrKSwgeSA9IG4sIGdyb3VwID0gaW50ZXJhY3Rpb24oc2Nob29sX3llYXIsYXMuZmFjdG9yKGNob2ljZXMpKSwgY29sb3VyID0gc2Nob29sX3llYXIpKSArCiAgZmFjZXRfZ3JpZChjb3Vyc2UgfiBjaG9pY2VzKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLlBPU0lYY3QoYygKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTAtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMTktMTItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDItMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDQtMDEgMDI6MDA6MDAgQ0VUIiwKICAgICAgICAgICAgICAgICAgICAgIjIwMjAtMDYtMDEgMDI6MDA6MDAgQ0VUIikpLAogICAgICAgICAgICAgICAgICAgbGltaXRzID0gYXMuUE9TSVhjdChjKCIyMDE5LTA5LTAxIDAyOjAwOjAwIENFVCIsICIyMDIwLTA3LTAxIDAyOjAwOjAwIENFVCIpKSwKICAgICAgICAgICAgICAgICAgIGRhdGVfbGFiZWxzID0gIiViIikgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIlRyaWFscyIsCiAgICAgICBjb2xvdXIgPSAiU2Nob29sIHllYXIiKSArCiAgdGhlbWVfcGFwZXIKCmBgYAoKYGBge3J9CnF1ZXN0aW9uX3R5cGVbLCAuKG4gPSBzdW0obikpLCBieSA9IC4oY291cnNlLCBtY3EgPSBjaG9pY2VzPjEsIHNjaG9vbF95ZWFyKV1bLCAuKHBlcmNfbWNxID0gblttY3EgPT0gVFJVRV0vc3VtKG4pKSwgYnkgPSAuKGNvdXJzZSwgc2Nob29sX3llYXIpXQpgYGAKClRoZXJlIGlzIGEgY2xlYXIgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBsYW5ndWFnZXMgaW4gdGhlIHF1ZXN0aW9uIGZvcm1hdCB1c2VkOiBFbmdsaXNoIHVzZXMgYWxtb3N0IGV4Y2x1c2l2ZWx5IDQtYWx0ZXJuYXRpdmUgTUNRcywgd2hpbGUgRnJlbmNoIHVzZXMgYSBtaXggb2YgTUNRcyAoaW5jbHVkaW5nIGEgc21hbGwgbnVtYmVyIG9mIDMtYWx0ZXJuYXRpdmUgcXVlc3Rpb25zKSBhbmQgb3Blbi1hbnN3ZXIgcXVlc3Rpb25zLgoKCiMgU2Vzc2lvbiBpbmZvCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoK